diff --git a/Makefile b/Makefile index 11b368c..de933b6 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,6 @@ GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@$(G XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest GOTESTSUM_PACKAGE ?= gotest.tools/gotestsum@latest -GENERATE ?= XGO_VERSION := go-1.22.x XGO_TARGETS ?= linux/amd64,linux/arm64 @@ -65,11 +64,7 @@ lint: golangci-lint .PHONY: generate generate: - $(GO) generate $(GENERATE) - -.PHONY: generate-docs -generate-docs: - $(GO) generate ./cmd/$(EXECUTABLE)/flags.go + $(GO) generate $(PACKAGES) .PHONY: test test: diff --git a/cmd/wp-opentofu/docs.go b/cmd/wp-opentofu/docs.go deleted file mode 100644 index de5412d..0000000 --- a/cmd/wp-opentofu/docs.go +++ /dev/null @@ -1,58 +0,0 @@ -//go:build generate -// +build generate - -package main - -import ( - "bytes" - "embed" - "fmt" - "os" - "text/template" - - "github.com/thegeeklab/wp-opentofu/plugin" - "github.com/thegeeklab/wp-plugin-go/docs" - wp "github.com/thegeeklab/wp-plugin-go/plugin" - wp_template "github.com/thegeeklab/wp-plugin-go/template" - "github.com/urfave/cli/v2" -) - -//go:embed templates/docs-data.yaml.tmpl -var yamlTemplate embed.FS - -func main() { - settings := &plugin.Settings{} - app := &cli.App{ - Flags: settingsFlags(settings, wp.FlagsPluginCategory), - } - - out, err := toYAML(app) - if err != nil { - panic(err) - } - - fi, err := os.Create("../../docs/data/data-raw.yaml") - if err != nil { - panic(err) - } - defer fi.Close() - if _, err := fi.WriteString(out); err != nil { - panic(err) - } -} - -func toYAML(app *cli.App) (string, error) { - var w bytes.Buffer - - yamlTmpl, err := template.New("docs").Funcs(wp_template.LoadFuncMap()).ParseFS(yamlTemplate, "templates/docs-data.yaml.tmpl") - if err != nil { - fmt.Println(yamlTmpl) - return "", err - } - - if err := yamlTmpl.ExecuteTemplate(&w, "docs-data.yaml.tmpl", docs.GetTemplateData(app)); err != nil { - return "", err - } - - return w.String(), nil -} diff --git a/cmd/wp-opentofu/flags.go b/cmd/wp-opentofu/flags.go deleted file mode 100644 index 6249d46..0000000 --- a/cmd/wp-opentofu/flags.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "github.com/thegeeklab/wp-opentofu/plugin" - "github.com/urfave/cli/v2" -) - -// SettingsFlags has the cli.Flags for the plugin.Settings. -// -//go:generate go run docs.go flags.go -func settingsFlags(settings *plugin.Settings, category string) []cli.Flag { - return []cli.Flag{ - &cli.StringSliceFlag{ - Name: "action", - Usage: "tofu actions to execute", - EnvVars: []string{"PLUGIN_ACTION"}, - Value: cli.NewStringSlice("validate", "plan", "apply"), - Destination: &settings.Action, - Category: category, - }, - &cli.StringFlag{ - Name: "init-option", - Usage: "tofu init command options, see https://opentofu.org/docs/cli/commands/init/", - EnvVars: []string{"PLUGIN_INIT_OPTION"}, - Category: category, - }, - &cli.StringFlag{ - Name: "fmt-option", - Usage: "options for the fmt command, see https://opentofu.org/docs/cli/commands/fmt/", - EnvVars: []string{"PLUGIN_FMT_OPTION"}, - Category: category, - }, - &cli.IntFlag{ - Name: "parallelism", - Usage: "number of concurrent operations", - EnvVars: []string{"PLUGIN_PARALLELISM"}, - Category: category, - }, - &cli.StringFlag{ - Name: "root-dir", - Usage: "root directory where the tofu files live", - EnvVars: []string{"PLUGIN_ROOT_DIR"}, - Destination: &settings.RootDir, - Category: category, - }, - &cli.BoolFlag{ - Name: "no-log", - Usage: "suppress tofu command output for `plan`, `apply` and `destroy` action", - EnvVars: []string{"PLUGIN_NO_LOG"}, - Destination: &settings.NoLog, - Category: category, - }, - &cli.StringSliceFlag{ - Name: "targets", - Usage: "targets to run `plan` or `apply` action on", - EnvVars: []string{"PLUGIN_TARGETS"}, - Destination: &settings.Targets, - Category: category, - }, - &cli.StringFlag{ - Name: "tofu-version", - Usage: "tofu version to use", - EnvVars: []string{"PLUGIN_TOFU_VERSION"}, - Destination: &settings.TofuVersion, - Category: category, - }, - &cli.BoolFlag{ - Name: "refresh", - Usage: "enables refreshing of the state before `plan` and `apply` commands", - EnvVars: []string{"PLUGIN_REFRESH"}, - Destination: &settings.Refresh, - Value: true, - Category: category, - }, - } -} diff --git a/cmd/wp-opentofu/main.go b/cmd/wp-opentofu/main.go index 7d69aab..6ed3c07 100644 --- a/cmd/wp-opentofu/main.go +++ b/cmd/wp-opentofu/main.go @@ -1,11 +1,7 @@ package main import ( - "fmt" - "github.com/thegeeklab/wp-opentofu/plugin" - - wp "github.com/thegeeklab/wp-plugin-go/plugin" ) //nolint:gochecknoglobals @@ -15,14 +11,5 @@ var ( ) func main() { - settings := &plugin.Settings{} - options := wp.Options{ - Name: "wp-opentofu", - Description: "Manage infrastructure with OpenTofu", - Version: BuildVersion, - VersionMetadata: fmt.Sprintf("date=%s", BuildDate), - Flags: settingsFlags(settings, wp.FlagsPluginCategory), - } - - plugin.New(options, settings).Run() + plugin.New(nil, BuildVersion, BuildDate).Run() } diff --git a/cmd/wp-opentofu/templates/docs-data.yaml.tmpl b/cmd/wp-opentofu/templates/docs-data.yaml.tmpl deleted file mode 100644 index e453a95..0000000 --- a/cmd/wp-opentofu/templates/docs-data.yaml.tmpl +++ /dev/null @@ -1,18 +0,0 @@ ---- -{{- if .GlobalArgs }} -properties: -{{- range $v := .GlobalArgs }} - - name: {{ $v.Name }} - {{- with $v.Description }} - description: | - {{ . | ToSentence }} - {{- end }} - {{- with $v.Type }} - type: {{ . }} - {{- end }} - {{- with $v.Default }} - defaultValue: {{ . }} - {{- end }} - required: {{ default false $v.Required }} -{{ end -}} -{{ end -}} diff --git a/docs/data/data.yaml b/docs/data/data.yaml index 97cee67..c208d35 100644 --- a/docs/data/data.yaml +++ b/docs/data/data.yaml @@ -19,6 +19,20 @@ properties: type: string required: false + - name: insecure_skip_verify + description: | + Skip SSL verification. + type: bool + defaultValue: false + required: false + + - name: log_level + description: | + Plugin log level. + type: string + defaultValue: "info" + required: false + - name: no_log description: | Suppress tofu command output for `plan`, `apply` and `destroy` action. diff --git a/go.mod b/go.mod index 6522e87..f15ec47 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22 require ( github.com/Masterminds/semver/v3 v3.2.1 github.com/rs/zerolog v1.32.0 - github.com/thegeeklab/wp-plugin-go v1.7.1 + github.com/thegeeklab/wp-plugin-go/v2 v2.0.0 github.com/urfave/cli/v2 v2.27.2 golang.org/x/sys v0.19.0 ) diff --git a/go.sum b/go.sum index a4b3a56..9c7c2ae 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/thegeeklab/wp-plugin-go v1.7.1 h1:zfR/rfNPuyVhXJu1fsLfp4+Mz2pTf6WwW/mIqw9750I= -github.com/thegeeklab/wp-plugin-go v1.7.1/go.mod h1:Ixi5plt9tpFGTu6yc/Inm5DcDpp3xPTeohfr86gf2EU= +github.com/thegeeklab/wp-plugin-go/v2 v2.0.0 h1:7dChME92qqpdtvDM4O2FZOoRv3jfkjlHCw/ijbE9SxQ= +github.com/thegeeklab/wp-plugin-go/v2 v2.0.0/go.mod h1:KRfDolkPSpO7Zx54Y0ofTFA7Cvd+7bHTHzYnYAo9WYg= github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= diff --git a/internal/docs/main.go b/internal/docs/main.go new file mode 100644 index 0000000..e591ae2 --- /dev/null +++ b/internal/docs/main.go @@ -0,0 +1,42 @@ +//go:build generate +// +build generate + +package main + +import ( + "context" + "flag" + "net/http" + "os" + "time" + + "github.com/thegeeklab/wp-opentofu/plugin" + "github.com/thegeeklab/wp-plugin-go/v2/docs" + wp_template "github.com/thegeeklab/wp-plugin-go/v2/template" +) + +func main() { + tmpl := "https://raw.githubusercontent.com/thegeeklab/woodpecker-plugins/main/templates/docs-data.yaml.tmpl" + client := http.Client{ + Timeout: 30 * time.Second, + } + + p := plugin.New(nil) + + out, err := wp_template.Render(context.Background(), client, tmpl, docs.GetTemplateData(p.App)) + if err != nil { + panic(err) + } + + outputFile := flag.String("output", "", "Output file path") + flag.Parse() + + if *outputFile == "" { + panic("no output file specified") + } + + err = os.WriteFile(*outputFile, []byte(out), 0o644) + if err != nil { + panic(err) + } +} diff --git a/plugin/impl.go b/plugin/impl.go index 6af1042..8e709a0 100644 --- a/plugin/impl.go +++ b/plugin/impl.go @@ -5,9 +5,10 @@ import ( "encoding/json" "errors" "fmt" + "io" "os" - "github.com/thegeeklab/wp-plugin-go/trace" + "github.com/thegeeklab/wp-plugin-go/v2/trace" ) var ( @@ -79,6 +80,9 @@ func (p *Plugin) Validate() error { // Execute provides the implementation of the plugin. func (p *Plugin) Execute() error { + batchCmd := make([]*Cmd, 0) + batchCmd = append(batchCmd, p.versionCommand()) + if p.Settings.TofuVersion != "" { err := installPackage(p.Plugin.Network.Context, p.Plugin.Network.Client, p.Settings.TofuVersion, maxDecompressionSize) if err != nil { @@ -86,27 +90,23 @@ func (p *Plugin) Execute() error { } } - commands := []*pluginCommand{ - p.versionCommand(), - } - - commands = append(commands, p.initCommand()) - commands = append(commands, p.getModulesCommand()) + batchCmd = append(batchCmd, p.initCommand()) + batchCmd = append(batchCmd, p.getModulesCommand()) for _, action := range p.Settings.Action.Value() { switch action { case "fmt": - commands = append(commands, p.fmtCommand()) + batchCmd = append(batchCmd, p.fmtCommand()) case "validate": - commands = append(commands, p.validateCommand()) + batchCmd = append(batchCmd, p.validateCommand()) case "plan": - commands = append(commands, p.planCommand(false)) + batchCmd = append(batchCmd, p.planCommand(false)) case "plan-destroy": - commands = append(commands, p.planCommand(true)) + batchCmd = append(batchCmd, p.planCommand(true)) case "apply": - commands = append(commands, p.applyCommand()) + batchCmd = append(batchCmd, p.applyCommand()) case "destroy": - commands = append(commands, p.destroyCommand()) + batchCmd = append(batchCmd, p.destroyCommand()) default: return fmt.Errorf("%w: %s", ErrActionUnknown, action) } @@ -116,18 +116,22 @@ func (p *Plugin) Execute() error { return err } - for _, command := range commands { - command.cmd.Stdout = os.Stdout - command.cmd.Stderr = os.Stderr - command.cmd.Env = os.Environ() + for _, bc := range batchCmd { + bc.Stdout = os.Stdout + bc.Stderr = os.Stderr + trace.Cmd(bc.Cmd) - if p.Settings.RootDir != "" { - command.cmd.Dir = p.Settings.RootDir + bc.Env = os.Environ() + + if bc.Private { + bc.Stdout = io.Discard } - trace.Cmd(command.cmd) + if p.Settings.RootDir != "" { + bc.Dir = p.Settings.RootDir + } - if err := command.cmd.Run(); err != nil { + if err := bc.Run(); err != nil { return err } } diff --git a/plugin/plugin.go b/plugin/plugin.go index 7ffca69..18d4495 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -1,10 +1,15 @@ package plugin import ( - wp "github.com/thegeeklab/wp-plugin-go/plugin" + "fmt" + + wp "github.com/thegeeklab/wp-plugin-go/v2/plugin" "github.com/urfave/cli/v2" + "golang.org/x/sys/execabs" ) +//go:generate go run ../internal/docs/main.go -output=../docs/data/data-raw.yaml + // Plugin implements provide the plugin. type Plugin struct { *wp.Plugin @@ -43,15 +48,104 @@ type FmtOptions struct { Check *bool `json:"check"` } -func New(options wp.Options, settings *Settings) *Plugin { - p := &Plugin{} +type Cmd struct { + *execabs.Cmd + Private bool +} - if options.Execute == nil { +func New(e wp.ExecuteFunc, build ...string) *Plugin { + p := &Plugin{ + Settings: &Settings{}, + } + + options := wp.Options{ + Name: "wp-opentofu", + Description: "Manage infrastructure with OpenTofu", + Flags: Flags(p.Settings, wp.FlagsPluginCategory), + HideWoodpeckerFlags: true, + } + + if len(build) > 0 { + options.Version = build[0] + } + + if len(build) > 1 { + options.VersionMetadata = fmt.Sprintf("date=%s", build[1]) + } + + if e == nil { options.Execute = p.run } p.Plugin = wp.New(options) - p.Settings = settings return p } + +// Flags returns a slice of CLI flags for the plugin. +func Flags(settings *Settings, category string) []cli.Flag { + return []cli.Flag{ + &cli.StringSliceFlag{ + Name: "action", + Usage: "tofu actions to execute", + EnvVars: []string{"PLUGIN_ACTION"}, + Value: cli.NewStringSlice("validate", "plan", "apply"), + Destination: &settings.Action, + Category: category, + }, + &cli.StringFlag{ + Name: "init-option", + Usage: "tofu init command options, see https://opentofu.org/docs/cli/commands/init/", + EnvVars: []string{"PLUGIN_INIT_OPTION"}, + Category: category, + }, + &cli.StringFlag{ + Name: "fmt-option", + Usage: "options for the fmt command, see https://opentofu.org/docs/cli/commands/fmt/", + EnvVars: []string{"PLUGIN_FMT_OPTION"}, + Category: category, + }, + &cli.IntFlag{ + Name: "parallelism", + Usage: "number of concurrent operations", + EnvVars: []string{"PLUGIN_PARALLELISM"}, + Category: category, + }, + &cli.StringFlag{ + Name: "root-dir", + Usage: "root directory where the tofu files live", + EnvVars: []string{"PLUGIN_ROOT_DIR"}, + Destination: &settings.RootDir, + Category: category, + }, + &cli.BoolFlag{ + Name: "no-log", + Usage: "suppress tofu command output for `plan`, `apply` and `destroy` action", + EnvVars: []string{"PLUGIN_NO_LOG"}, + Destination: &settings.NoLog, + Category: category, + }, + &cli.StringSliceFlag{ + Name: "targets", + Usage: "targets to run `plan` or `apply` action on", + EnvVars: []string{"PLUGIN_TARGETS"}, + Destination: &settings.Targets, + Category: category, + }, + &cli.StringFlag{ + Name: "tofu-version", + Usage: "tofu version to use", + EnvVars: []string{"PLUGIN_TOFU_VERSION"}, + Destination: &settings.TofuVersion, + Category: category, + }, + &cli.BoolFlag{ + Name: "refresh", + Usage: "enables refreshing of the state before `plan` and `apply` commands", + EnvVars: []string{"PLUGIN_REFRESH"}, + Destination: &settings.Refresh, + Value: true, + Category: category, + }, + } +} diff --git a/plugin/tofu.go b/plugin/tofu.go index 05a4151..e4c2e0c 100644 --- a/plugin/tofu.go +++ b/plugin/tofu.go @@ -10,19 +10,14 @@ const ( tofuBin = "/usr/local/bin/tofu" ) -type pluginCommand struct { - cmd *execabs.Cmd - private bool -} - -func (p *Plugin) versionCommand() *pluginCommand { - return &pluginCommand{ - execabs.Command(tofuBin, "version"), - false, +func (p *Plugin) versionCommand() *Cmd { + return &Cmd{ + Cmd: execabs.Command(tofuBin, "version"), + Private: p.Settings.NoLog, } } -func (p *Plugin) initCommand() *pluginCommand { +func (p *Plugin) initCommand() *Cmd { args := []string{ "init", } @@ -34,27 +29,24 @@ func (p *Plugin) initCommand() *pluginCommand { // Fail tofu execution on prompt args = append(args, "-input=false") - return &pluginCommand{ - execabs.Command(tofuBin, args...), - false, + return &Cmd{ + Cmd: execabs.Command(tofuBin, args...), } } -func (p *Plugin) getModulesCommand() *pluginCommand { - return &pluginCommand{ - execabs.Command(tofuBin, "get"), - false, +func (p *Plugin) getModulesCommand() *Cmd { + return &Cmd{ + Cmd: execabs.Command(tofuBin, "get"), } } -func (p *Plugin) validateCommand() *pluginCommand { - return &pluginCommand{ - execabs.Command(tofuBin, "validate"), - false, +func (p *Plugin) validateCommand() *Cmd { + return &Cmd{ + Cmd: execabs.Command(tofuBin, "validate"), } } -func (p *Plugin) fmtCommand() *pluginCommand { +func (p *Plugin) fmtCommand() *Cmd { args := []string{ "fmt", } @@ -75,13 +67,12 @@ func (p *Plugin) fmtCommand() *pluginCommand { args = append(args, fmt.Sprintf("-check=%t", *p.Settings.FmtOptions.Check)) } - return &pluginCommand{ - execabs.Command(tofuBin, args...), - false, + return &Cmd{ + Cmd: execabs.Command(tofuBin, args...), } } -func (p *Plugin) planCommand(destroy bool) *pluginCommand { +func (p *Plugin) planCommand(destroy bool) *Cmd { args := []string{ "plan", } @@ -112,13 +103,13 @@ func (p *Plugin) planCommand(destroy bool) *pluginCommand { args = append(args, "-refresh=false") } - return &pluginCommand{ - execabs.Command(tofuBin, args...), - p.Settings.NoLog, + return &Cmd{ + Cmd: execabs.Command(tofuBin, args...), + Private: p.Settings.NoLog, } } -func (p *Plugin) applyCommand() *pluginCommand { +func (p *Plugin) applyCommand() *Cmd { args := []string{ "apply", } @@ -145,13 +136,13 @@ func (p *Plugin) applyCommand() *pluginCommand { args = append(args, p.Settings.OutFile) - return &pluginCommand{ - execabs.Command(tofuBin, args...), - p.Settings.NoLog, + return &Cmd{ + Cmd: execabs.Command(tofuBin, args...), + Private: p.Settings.NoLog, } } -func (p *Plugin) destroyCommand() *pluginCommand { +func (p *Plugin) destroyCommand() *Cmd { args := []string{ "destroy", } @@ -174,8 +165,8 @@ func (p *Plugin) destroyCommand() *pluginCommand { args = append(args, "-auto-approve") - return &pluginCommand{ - execabs.Command(tofuBin, args...), - p.Settings.NoLog, + return &Cmd{ + Cmd: execabs.Command(tofuBin, args...), + Private: p.Settings.NoLog, } }