Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support write to local file instead of post on github #654

Merged
merged 13 commits into from
Feb 15, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/cli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func New(flags *LDFlags) *cli.App {
&cli.IntFlag{Name: "pr", Usage: "pull request number"},
&cli.StringFlag{Name: "config", Usage: "config path"},
&cli.StringSliceFlag{Name: "var", Usage: "template variables. The format of value is '<name>:<value>'"},
&cli.StringFlag{Name: "output", Usage: "specify file to output result instead of post comment"},
}
app.Commands = []*cli.Command{
{
Expand Down
4 changes: 4 additions & 0 deletions pkg/cli/var.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ func parseOpts(ctx *cli.Context, cfg *config.Config) error {
cfg.CI.Link = buildURL
}

if output := ctx.String("output"); output != "" {
cfg.Output = output
}

vars := ctx.StringSlice("var")
vm := make(map[string]string, len(vars))
if err := parseVarOpts(vars, vm); err != nil {
Expand Down
5 changes: 5 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Config struct {
PlanPatch bool `yaml:"plan_patch"`
RepoOwner string `yaml:"repo_owner"`
RepoName string `yaml:"repo_name"`
Output string `yaml:"output_file"`
NikitaCOEUR marked this conversation as resolved.
Show resolved Hide resolved
}

type CI struct {
Expand Down Expand Up @@ -107,6 +108,10 @@ func (cfg *Config) LoadFile(path string) error {

// Validate validates config file
func (cfg *Config) Validate() error {

NikitaCOEUR marked this conversation as resolved.
Show resolved Hide resolved
if cfg.Output != "" {
return nil
}
if cfg.CI.Owner == "" {
return errors.New("repository owner is missing")
}
Expand Down
68 changes: 44 additions & 24 deletions pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/suzuki-shunsuke/tfcmt/pkg/config"
"github.com/suzuki-shunsuke/tfcmt/pkg/notifier"
"github.com/suzuki-shunsuke/tfcmt/pkg/notifier/github"
"github.com/suzuki-shunsuke/tfcmt/pkg/notifier/localFile"
tmpl "github.com/suzuki-shunsuke/tfcmt/pkg/template"
"github.com/suzuki-shunsuke/tfcmt/pkg/terraform"
)
Expand Down Expand Up @@ -130,29 +131,48 @@ func (ctrl *Controller) getNotifier(ctx context.Context) (notifier.Notifier, err
}
labels = a
}
client, err := github.NewClient(ctx, &github.Config{
Token: ctrl.Config.GitHubToken,
BaseURL: ctrl.Config.GHEBaseURL,
GraphQLEndpoint: ctrl.Config.GHEGraphQLEndpoint,
Owner: ctrl.Config.CI.Owner,
Repo: ctrl.Config.CI.Repo,
PR: github.PullRequest{
Revision: ctrl.Config.CI.SHA,
Number: ctrl.Config.CI.PRNumber,
},
CI: ctrl.Config.CI.Link,
Parser: ctrl.Parser,
UseRawOutput: ctrl.Config.Terraform.UseRawOutput,
Template: ctrl.Template,
ParseErrorTemplate: ctrl.ParseErrorTemplate,
ResultLabels: labels,
Vars: ctrl.Config.Vars,
EmbeddedVarNames: ctrl.Config.EmbeddedVarNames,
Templates: ctrl.Config.Templates,
Patch: ctrl.Config.PlanPatch,
})
if err != nil {
return nil, err
// Write output to file instead of github comment
if ctrl.Config.Output != "" {
client, err := localFile.NewClient(ctx, &localFile.Config{
OutputFile: ctrl.Config.Output,
Parser: ctrl.Parser,
UseRawOutput: ctrl.Config.Terraform.UseRawOutput,
Template: ctrl.Template,
ParseErrorTemplate: ctrl.ParseErrorTemplate,
Vars: ctrl.Config.Vars,
EmbeddedVarNames: ctrl.Config.EmbeddedVarNames,
Templates: ctrl.Config.Templates,
})
if err != nil {
return nil, err
}
return client.Notify, nil
} else {
client, err := github.NewClient(ctx, &github.Config{
Token: ctrl.Config.GitHubToken,
BaseURL: ctrl.Config.GHEBaseURL,
GraphQLEndpoint: ctrl.Config.GHEGraphQLEndpoint,
Owner: ctrl.Config.CI.Owner,
Repo: ctrl.Config.CI.Repo,
PR: github.PullRequest{
Revision: ctrl.Config.CI.SHA,
Number: ctrl.Config.CI.PRNumber,
},
CI: ctrl.Config.CI.Link,
Parser: ctrl.Parser,
UseRawOutput: ctrl.Config.Terraform.UseRawOutput,
Template: ctrl.Template,
ParseErrorTemplate: ctrl.ParseErrorTemplate,
ResultLabels: labels,
Vars: ctrl.Config.Vars,
EmbeddedVarNames: ctrl.Config.EmbeddedVarNames,
Templates: ctrl.Config.Templates,
Patch: ctrl.Config.PlanPatch,
})
if err != nil {
return nil, err
}
return client.Notify, nil
}
return client.Notify, nil

}
64 changes: 64 additions & 0 deletions pkg/notifier/localFile/apply.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package localFile

import (
"context"

"github.com/sirupsen/logrus"
"github.com/suzuki-shunsuke/tfcmt/pkg/notifier"
"github.com/suzuki-shunsuke/tfcmt/pkg/terraform"
)

// Apply posts comment optimized for notifications
func (g *NotifyService) Apply(ctx context.Context, param *notifier.ParamExec) (int, error) {
cfg := g.client.Config
parser := g.client.Config.Parser
template := g.client.Config.Template
var errMsgs []string

result := parser.Parse(param.CombinedOutput)
result.ExitCode = param.ExitCode
if result.HasParseError {
template = g.client.Config.ParseErrorTemplate
} else {
if result.Error != nil {
return result.ExitCode, result.Error
}
if result.Result == "" {
return result.ExitCode, result.Error
}
}

template.SetValue(terraform.CommonTemplate{
Result: result.Result,
ChangedResult: result.ChangedResult,
ChangeOutsideTerraform: result.OutsideTerraform,
Warning: result.Warning,
HasDestroy: result.HasDestroy,
UseRawOutput: cfg.UseRawOutput,
Vars: cfg.Vars,
Templates: cfg.Templates,
Stdout: param.Stdout,
Stderr: param.Stderr,
CombinedOutput: param.CombinedOutput,
ExitCode: param.ExitCode,
ErrorMessages: errMsgs,
CreatedResources: result.CreatedResources,
UpdatedResources: result.UpdatedResources,
DeletedResources: result.DeletedResources,
ReplacedResources: result.ReplacedResources,
})
body, err := template.Execute()
if err != nil {
return result.ExitCode, err
}

logE := logrus.WithFields(logrus.Fields{
"program": "tfcmt",
})

logE.Debug("write output apply to file")
if err := g.client.Comment.Post(ctx, body, cfg.OutputFile); err != nil {
return result.ExitCode, err
}
return result.ExitCode, nil
}
49 changes: 49 additions & 0 deletions pkg/notifier/localFile/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package localFile

import (
"context"

"github.com/suzuki-shunsuke/tfcmt/pkg/terraform"
)

// Client is a fake API client for write to local file
type Client struct {
Debug bool

Config *Config

common service

Notify *NotifyService
Comment *LocalFileService
}

// Config is a configuration for local file
type Config struct {
OutputFile string
Parser terraform.Parser
// Template is used for all Terraform command output
Template *terraform.Template
ParseErrorTemplate *terraform.Template
Vars map[string]string
EmbeddedVarNames []string
Templates map[string]string
UseRawOutput bool
}

type service struct {
client *Client
}

// NewClient returns Client initialized with Config
func NewClient(ctx context.Context, cfg *Config) (*Client, error) {
c := &Client{
Config: cfg,
}

c.common.client = c

c.Notify = (*NotifyService)(&c.common)

return c, nil
}
36 changes: 36 additions & 0 deletions pkg/notifier/localFile/comment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package localFile

import (
"context"
"os"

"github.com/sirupsen/logrus"
)

type LocalFileService service

// Post posts comment
func (f *LocalFileService) Post(ctx context.Context, body string, OutputFile string) error {
logE := logrus.WithFields(logrus.Fields{
"program": "tfcmt",
})


file, err := os.Create(OutputFile)

if err != nil {
return err
}


defer file.Close()

_, err2 := file.WriteString(body)

if err2 != nil {
return err2
}
logE.Debug("Output to file success")

return err
}
5 changes: 5 additions & 0 deletions pkg/notifier/localFile/notify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package localFile

// NotifyService handles communication with the notification related
// methods of GitHub API
type NotifyService service
65 changes: 65 additions & 0 deletions pkg/notifier/localFile/plan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package localFile

import (
"context"

"github.com/sirupsen/logrus"
"github.com/suzuki-shunsuke/tfcmt/pkg/notifier"
"github.com/suzuki-shunsuke/tfcmt/pkg/terraform"
)

// Plan posts comment optimized for notifications
func (g *NotifyService) Plan(ctx context.Context, param *notifier.ParamExec) (int, error) { //nolint:cyclop
cfg := g.client.Config
parser := g.client.Config.Parser
template := g.client.Config.Template
var errMsgs []string

result := parser.Parse(param.CombinedOutput)
result.ExitCode = param.ExitCode
if result.HasParseError {
template = g.client.Config.ParseErrorTemplate
} else {
if result.Error != nil {
return result.ExitCode, result.Error
}
if result.Result == "" {
return result.ExitCode, result.Error
}
}

template.SetValue(terraform.CommonTemplate{
Result: result.Result,
ChangedResult: result.ChangedResult,
ChangeOutsideTerraform: result.OutsideTerraform,
Warning: result.Warning,
HasDestroy: result.HasDestroy,
UseRawOutput: cfg.UseRawOutput,
Vars: cfg.Vars,
Templates: cfg.Templates,
Stdout: param.Stdout,
Stderr: param.Stderr,
CombinedOutput: param.CombinedOutput,
ExitCode: param.ExitCode,
ErrorMessages: errMsgs,
CreatedResources: result.CreatedResources,
UpdatedResources: result.UpdatedResources,
DeletedResources: result.DeletedResources,
ReplacedResources: result.ReplacedResources,
})
body, err := template.Execute()
if err != nil {
return result.ExitCode, err
}

logE := logrus.WithFields(logrus.Fields{
"program": "tfcmt",
})

logE.Debug("write output plan to file")
// WriteFile to Outputfile define in config (via command -output)
if err := g.client.Comment.Post(ctx, body, cfg.OutputFile); err != nil {
return result.ExitCode, err
}
return result.ExitCode, nil
}