diff --git a/Makefile b/Makefile index 8e28f7d..ab9c572 100644 --- a/Makefile +++ b/Makefile @@ -24,9 +24,9 @@ GOPKGS ?= $(shell $(GOCMD) list $(MODVENDOR) ./... | grep -v /vendor) MODVENDOR := -mod=vendor GOLDFLAGS :=" -GOLDFLAGS += -X $(PACKAGE)/internal/pkg/version.version=$(VERSION) -GOLDFLAGS += -X $(PACKAGE)/internal/pkg/version.commitHash=$(COMMIT_HASH) -GOLDFLAGS += -X $(PACKAGE)/internal/pkg/version.buildDate=$(BUILD_DATE) +GOLDFLAGS += -X $(PACKAGE)/internal/version.version=$(VERSION) +GOLDFLAGS += -X $(PACKAGE)/internal/version.commitHash=$(COMMIT_HASH) +GOLDFLAGS += -X $(PACKAGE)/internal/version.buildDate=$(BUILD_DATE) GOLDFLAGS +=" GOBUILD ?= CGO_ENABLED=0 $(GOCMD) build $(MODVENDOR) -ldflags $(GOLDFLAGS) @@ -147,19 +147,27 @@ changelog: ## Generate Changelog .PHONY: git-chglog git-chglog: +ifeq (, $(shell which git-chglog)) curl -sfL https://github.com/git-chglog/git-chglog/releases/download/$(GITCHGLOG_VERSION)/git-chglog_$(shell go env GOOS)_$(shell go env GOARCH) -o $(shell go env GOPATH)/bin/git-chglog && chmod +x $(shell go env GOPATH)/bin/git-chglog +endif .PHONY: goimports goimports: +ifeq (, $(shell which goimports)) GO111MODULE=off go get -u golang.org/x/tools/cmd/goimports +endif .PHONY: golangci golangci: +ifeq (, $(shell which golangci-lint)) curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(shell go env GOPATH)/bin $(GOLANGCI_VERSION) +endif .PHONY: gox gox: +ifeq (, $(shell which gox)) GO111MODULE=off go get -u github.com/mitchellh/gox +endif .PHONY: tools tools: ## Install required tools diff --git a/cmd/json.go b/cmd/json.go index a389a6a..87f7613 100644 --- a/cmd/json.go +++ b/cmd/json.go @@ -1,8 +1,7 @@ package cmd import ( - "github.com/segmentio/terraform-docs/internal/pkg/print/json" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/internal/format" "github.com/spf13/cobra" ) @@ -10,13 +9,13 @@ var jsonCmd = &cobra.Command{ Args: cobra.ExactArgs(1), Use: "json [PATH]", Short: "Generate JSON of inputs and outputs", - Run: func(cmd *cobra.Command, args []string) { - doPrint(args[0], func(module *tfconf.Module) (string, error) { - return json.Print(module, settings) - }) + RunE: func(cmd *cobra.Command, args []string) error { + return doPrint(args[0], format.NewJSON(settings)) }, } func init() { + jsonCmd.PersistentFlags().BoolVar(new(bool), "no-escape", false, "do not escape special characters") + rootCmd.AddCommand(jsonCmd) } diff --git a/cmd/markdown.go b/cmd/markdown.go index 9073100..3f68f27 100644 --- a/cmd/markdown.go +++ b/cmd/markdown.go @@ -1,9 +1,7 @@ package cmd import ( - "github.com/segmentio/terraform-docs/internal/pkg/print/markdown/document" - "github.com/segmentio/terraform-docs/internal/pkg/print/markdown/table" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/internal/format" "github.com/spf13/cobra" ) @@ -12,10 +10,8 @@ var markdownCmd = &cobra.Command{ Use: "markdown [PATH]", Aliases: []string{"md"}, Short: "Generate Markdown of inputs and outputs", - Run: func(cmd *cobra.Command, args []string) { - doPrint(args[0], func(module *tfconf.Module) (string, error) { - return table.Print(module, settings) - }) + RunE: func(cmd *cobra.Command, args []string) error { + return doPrint(args[0], format.NewTable(settings)) }, } @@ -24,10 +20,8 @@ var mdTableCmd = &cobra.Command{ Use: "table [PATH]", Aliases: []string{"tbl"}, Short: "Generate Markdown tables of inputs and outputs", - Run: func(cmd *cobra.Command, args []string) { - doPrint(args[0], func(module *tfconf.Module) (string, error) { - return table.Print(module, settings) - }) + RunE: func(cmd *cobra.Command, args []string) error { + return doPrint(args[0], format.NewTable(settings)) }, } @@ -36,14 +30,16 @@ var mdDocumentCmd = &cobra.Command{ Use: "document [PATH]", Aliases: []string{"doc"}, Short: "Generate Markdown document of inputs and outputs", - Run: func(cmd *cobra.Command, args []string) { - doPrint(args[0], func(module *tfconf.Module) (string, error) { - return document.Print(module, settings) - }) + RunE: func(cmd *cobra.Command, args []string) error { + return doPrint(args[0], format.NewDocument(settings)) }, } func init() { + markdownCmd.PersistentFlags().BoolVar(new(bool), "no-required", false, "do not show \"Required\" column or section") + markdownCmd.PersistentFlags().BoolVar(new(bool), "no-escape", false, "do not escape special characters") + markdownCmd.PersistentFlags().IntVar(&settings.MarkdownIndent, "indent", 2, "indention level of Markdown sections [1, 2, 3, 4, 5]") + markdownCmd.AddCommand(mdTableCmd) markdownCmd.AddCommand(mdDocumentCmd) diff --git a/cmd/pretty.go b/cmd/pretty.go index 4a93797..3167850 100644 --- a/cmd/pretty.go +++ b/cmd/pretty.go @@ -1,8 +1,7 @@ package cmd import ( - "github.com/segmentio/terraform-docs/internal/pkg/print/pretty" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/internal/format" "github.com/spf13/cobra" ) @@ -10,13 +9,13 @@ var prettyCmd = &cobra.Command{ Args: cobra.ExactArgs(1), Use: "pretty [PATH]", Short: "Generate colorized pretty of inputs and outputs", - Run: func(cmd *cobra.Command, args []string) { - doPrint(args[0], func(module *tfconf.Module) (string, error) { - return pretty.Print(module, settings) - }) + RunE: func(cmd *cobra.Command, args []string) error { + return doPrint(args[0], format.NewPretty(settings)) }, } func init() { + prettyCmd.PersistentFlags().BoolVar(new(bool), "no-color", false, "do not colorize printed result") + rootCmd.AddCommand(prettyCmd) } diff --git a/cmd/root.go b/cmd/root.go index 2ab9450..6ae132a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,16 +2,15 @@ package cmd import ( "fmt" - "log" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" - "github.com/segmentio/terraform-docs/internal/pkg/version" + "github.com/segmentio/terraform-docs/internal/module" + "github.com/segmentio/terraform-docs/internal/version" + "github.com/segmentio/terraform-docs/pkg/print" "github.com/spf13/cobra" ) var settings = print.NewSettings() -var options = tfconf.Options{} +var options = module.NewOptions() // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ @@ -21,27 +20,19 @@ var rootCmd = &cobra.Command{ Long: "A utility to generate documentation from Terraform modules in various output formats", Version: version.Version(), PersistentPreRun: func(cmd *cobra.Command, args []string) { - noheader, _ := cmd.Flags().GetBool("no-header") - noproviders, _ := cmd.Flags().GetBool("no-providers") - noinputs, _ := cmd.Flags().GetBool("no-inputs") - nooutputs, _ := cmd.Flags().GetBool("no-outputs") + oppositeBool := func(name string) bool { + val, _ := cmd.Flags().GetBool(name) + return !val + } + settings.ShowHeader = oppositeBool("no-header") + settings.ShowProviders = oppositeBool("no-providers") + settings.ShowInputs = oppositeBool("no-inputs") + settings.ShowOutputs = oppositeBool("no-outputs") - nocolor, _ := cmd.Flags().GetBool("no-color") - nosort, _ := cmd.Flags().GetBool("no-sort") - norequired, _ := cmd.Flags().GetBool("no-required") - noescape, _ := cmd.Flags().GetBool("no-escape") - - settings.ShowHeader = !noheader - settings.ShowProviders = !noproviders - settings.ShowInputs = !noinputs - settings.ShowOutputs = !nooutputs - - settings.OutputValues = options.OutputValues - - settings.ShowColor = !nocolor - settings.SortByName = !nosort - settings.ShowRequired = !norequired - settings.EscapeCharacters = !noescape + settings.ShowColor = oppositeBool("no-color") + settings.SortByName = oppositeBool("no-sort") + settings.ShowRequired = oppositeBool("no-required") + settings.EscapeCharacters = oppositeBool("no-escape") }, } @@ -63,14 +54,6 @@ func init() { rootCmd.PersistentFlags().BoolVar(&settings.SortByRequired, "sort-inputs-by-required", false, "[deprecated] use '--sort-by-required' instead") rootCmd.PersistentFlags().BoolVar(new(bool), "with-aggregate-type-defaults", false, "[deprecated] print default values of aggregate types") //----------------------------- - - markdownCmd.PersistentFlags().BoolVar(new(bool), "no-required", false, "do not show \"Required\" column or section") - markdownCmd.PersistentFlags().BoolVar(new(bool), "no-escape", false, "do not escape special characters") - markdownCmd.PersistentFlags().IntVar(&settings.MarkdownIndent, "indent", 2, "indention level of Markdown sections [1, 2, 3, 4, 5]") - - prettyCmd.PersistentFlags().BoolVar(new(bool), "no-color", false, "do not colorize printed result") - - jsonCmd.PersistentFlags().BoolVar(new(bool), "no-escape", false, "do not escape special characters") } // Execute adds all child commands to the root command and sets flags appropriately. @@ -92,18 +75,22 @@ func FormatterCmds() []*cobra.Command { } } -func doPrint(path string, fn func(*tfconf.Module) (string, error)) { - options.Path = path - module, err := tfconf.CreateModule(&options) +func doPrint(path string, printer print.Format) error { + options.With(&module.Options{ + Path: path, + SortBy: &module.SortBy{ + Name: settings.SortByName, + Required: settings.SortByRequired, + }, + }) + tfmodule, err := module.LoadWithOptions(options) if err != nil { - log.Fatal(err) + return err } - - output, err := fn(module) - + output, err := printer.Print(tfmodule, settings) if err != nil { - log.Fatal(err) + return err } - fmt.Println(output) + return nil } diff --git a/cmd/version.go b/cmd/version.go index 97864d6..457d63c 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -3,7 +3,7 @@ package cmd import ( "fmt" - "github.com/segmentio/terraform-docs/internal/pkg/version" + "github.com/segmentio/terraform-docs/internal/version" "github.com/spf13/cobra" ) diff --git a/cmd/yaml.go b/cmd/yaml.go index f86fb44..854cb85 100644 --- a/cmd/yaml.go +++ b/cmd/yaml.go @@ -1,8 +1,7 @@ package cmd import ( - "github.com/segmentio/terraform-docs/internal/pkg/print/yaml" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/internal/format" "github.com/spf13/cobra" ) @@ -10,10 +9,8 @@ var yamlCmd = &cobra.Command{ Args: cobra.ExactArgs(1), Use: "yaml [PATH]", Short: "Generate YAML of inputs and outputs", - Run: func(cmd *cobra.Command, args []string) { - doPrint(args[0], func(module *tfconf.Module) (string, error) { - return yaml.Print(module, settings) - }) + RunE: func(cmd *cobra.Command, args []string) error { + return doPrint(args[0], format.NewYAML(settings)) }, } diff --git a/internal/pkg/print/markdown/document/document.go b/internal/format/document.go similarity index 72% rename from internal/pkg/print/markdown/document/document.go rename to internal/format/document.go index 38cc7e0..a057862 100644 --- a/internal/pkg/print/markdown/document/document.go +++ b/internal/format/document.go @@ -1,16 +1,15 @@ -package document +package format import ( "text/template" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/print/markdown" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" - "github.com/segmentio/terraform-docs/internal/pkg/tmpl" + "github.com/segmentio/terraform-docs/pkg/print" + "github.com/segmentio/terraform-docs/pkg/tfconf" + "github.com/segmentio/terraform-docs/pkg/tmpl" ) const ( - headerTpl = ` + documentHeaderTpl = ` {{- if .Settings.ShowHeader -}} {{- with .Module.Header -}} {{ sanitizeHeader . }} @@ -19,7 +18,7 @@ const ( {{ end -}} ` - providersTpl = ` + documentProvidersTpl = ` {{- if .Settings.ShowProviders -}} {{ indent 0 }} Providers {{ if not .Module.Providers }} @@ -34,7 +33,7 @@ const ( {{ end -}} ` - inputsTpl = ` + documentInputsTpl = ` {{- if .Settings.ShowInputs -}} {{- if .Settings.ShowRequired -}} {{ indent 0 }} Required Inputs @@ -69,7 +68,7 @@ const ( {{ end -}} ` - inputTpl = ` + documentInputTpl = ` {{ printf "\n" }} {{ indent 1 }} {{ name .Name }} @@ -82,7 +81,7 @@ const ( {{- end }} ` - outputsTpl = ` + documentOutputsTpl = ` {{- if .Settings.ShowOutputs -}} {{ indent 0 }} Outputs {{ if not .Module.Outputs }} @@ -110,33 +109,36 @@ const ( ` ) -// Print prints a document as Markdown document. -func Print(module *tfconf.Module, settings *print.Settings) (string, error) { - module.Sort(settings) +// Document represents Markdown Document format. +type Document struct { + template *tmpl.Template +} - t := tmpl.NewTemplate(&tmpl.Item{ +// NewDocument returns new instance of Document. +func NewDocument(settings *print.Settings) *Document { + tt := tmpl.NewTemplate(&tmpl.Item{ Name: "document", Text: documentTpl, }, &tmpl.Item{ Name: "header", - Text: headerTpl, + Text: documentHeaderTpl, }, &tmpl.Item{ Name: "providers", - Text: providersTpl, + Text: documentProvidersTpl, }, &tmpl.Item{ Name: "inputs", - Text: inputsTpl, + Text: documentInputsTpl, }, &tmpl.Item{ Name: "input", - Text: inputTpl, + Text: documentInputTpl, }, &tmpl.Item{ Name: "outputs", - Text: outputsTpl, + Text: documentOutputsTpl, }) - t.Settings(settings) - t.CustomFunc(template.FuncMap{ + tt.Settings(settings) + tt.CustomFunc(template.FuncMap{ "type": func(t string) string { - result, extraline := markdown.PrintFencedCodeBlock(t, "hcl") + result, extraline := printFencedCodeBlock(t, "hcl") if !extraline { result += "\n" } @@ -146,7 +148,7 @@ func Print(module *tfconf.Module, settings *print.Settings) (string, error) { if v == "n/a" { return v } - result, extraline := markdown.PrintFencedCodeBlock(v, "json") + result, extraline := printFencedCodeBlock(v, "json") if !extraline { result += "\n" } @@ -156,10 +158,16 @@ func Print(module *tfconf.Module, settings *print.Settings) (string, error) { return settings.ShowRequired }, }) - rendered, err := t.Render(module) + return &Document{ + template: tt, + } +} + +// Print prints a Terraform module as Markdown document. +func (d *Document) Print(module *tfconf.Module, settings *print.Settings) (string, error) { + rendered, err := d.template.Render(module) if err != nil { return "", err } - - return markdown.Sanitize(rendered), nil + return sanitize(rendered), nil } diff --git a/internal/pkg/print/markdown/document/document_test.go b/internal/format/document_test.go similarity index 54% rename from internal/pkg/print/markdown/document/document_test.go rename to internal/format/document_test.go index 95e0725..808df38 100644 --- a/internal/pkg/print/markdown/document/document_test.go +++ b/internal/format/document_test.go @@ -1,11 +1,11 @@ -package document +package format import ( "testing" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/testutil" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/internal/module" + "github.com/segmentio/terraform-docs/internal/testutil" + "github.com/segmentio/terraform-docs/pkg/print" "github.com/stretchr/testify/assert" ) @@ -13,13 +13,15 @@ func TestDocument(t *testing.T) { assert := assert.New(t) settings := testutil.Settings().WithSections().Build() - expected, err := testutil.GetExpected("document") + expected, err := testutil.GetExpected("document", "document") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -31,13 +33,15 @@ func TestDocumentWithRequired(t *testing.T) { ShowRequired: true, }).Build() - expected, err := testutil.GetExpected("document-WithRequired") + expected, err := testutil.GetExpected("document", "document-WithRequired") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -49,13 +53,19 @@ func TestDocumentSortByName(t *testing.T) { SortByName: true, }).Build() - expected, err := testutil.GetExpected("document-SortByName") + expected, err := testutil.GetExpected("document", "document-SortByName") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions().With(&module.Options{ + SortBy: &module.SortBy{ + Name: true, + }, + }) + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -68,13 +78,20 @@ func TestDocumentSortByRequired(t *testing.T) { SortByRequired: true, }).Build() - expected, err := testutil.GetExpected("document-SortByRequired") + expected, err := testutil.GetExpected("document", "document-SortByRequired") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions().With(&module.Options{ + SortBy: &module.SortBy{ + Name: true, + Required: true, + }, + }) + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -89,13 +106,15 @@ func TestDocumentNoHeader(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("document-NoHeader") + expected, err := testutil.GetExpected("document", "document-NoHeader") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -110,13 +129,15 @@ func TestDocumentNoProviders(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("document-NoProviders") + expected, err := testutil.GetExpected("document", "document-NoProviders") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -131,13 +152,15 @@ func TestDocumentNoInputs(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("document-NoInputs") + expected, err := testutil.GetExpected("document", "document-NoInputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -152,13 +175,15 @@ func TestDocumentNoOutputs(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("document-NoOutputs") + expected, err := testutil.GetExpected("document", "document-NoOutputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -173,13 +198,15 @@ func TestDocumentOnlyHeader(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("document-OnlyHeader") + expected, err := testutil.GetExpected("document", "document-OnlyHeader") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -194,13 +221,15 @@ func TestDocumentOnlyProviders(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("document-OnlyProviders") + expected, err := testutil.GetExpected("document", "document-OnlyProviders") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -215,13 +244,15 @@ func TestDocumentOnlyInputs(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("document-OnlyInputs") + expected, err := testutil.GetExpected("document", "document-OnlyInputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -236,13 +267,15 @@ func TestDocumentOnlyOutputs(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("document-OnlyOutputs") + expected, err := testutil.GetExpected("document", "document-OnlyOutputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -254,13 +287,15 @@ func TestDocumentEscapeCharacters(t *testing.T) { EscapeCharacters: true, }).Build() - expected, err := testutil.GetExpected("document-EscapeCharacters") + expected, err := testutil.GetExpected("document", "document-EscapeCharacters") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -272,13 +307,15 @@ func TestDocumentIndentationBelowAllowed(t *testing.T) { MarkdownIndent: 0, }).Build() - expected, err := testutil.GetExpected("document-IndentationBelowAllowed") + expected, err := testutil.GetExpected("document", "document-IndentationBelowAllowed") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -290,13 +327,15 @@ func TestDocumentIndentationAboveAllowed(t *testing.T) { MarkdownIndent: 10, }).Build() - expected, err := testutil.GetExpected("document-IndentationAboveAllowed") + expected, err := testutil.GetExpected("document", "document-IndentationAboveAllowed") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -308,13 +347,15 @@ func TestDocumentIndentationOfFour(t *testing.T) { MarkdownIndent: 4, }).Build() - expected, err := testutil.GetExpected("document-IndentationOfFour") + expected, err := testutil.GetExpected("document", "document-IndentationOfFour") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -326,17 +367,18 @@ func TestDocumentOutputValues(t *testing.T) { OutputValues: true, }).Build() - expected, err := testutil.GetExpected("document-OutputValues") + expected, err := testutil.GetExpected("document", "document-OutputValues") assert.Nil(err) - options := &tfconf.Options{ + options := module.NewOptions().With(&module.Options{ OutputValues: true, OutputValuesPath: "output_values.json", - } + }) module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewDocument(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/pkg/print/json/json.go b/internal/format/json.go similarity index 64% rename from internal/pkg/print/json/json.go rename to internal/format/json.go index 72c6f9d..d0c3107 100644 --- a/internal/pkg/print/json/json.go +++ b/internal/format/json.go @@ -1,18 +1,24 @@ -package json +package format import ( "bytes" "encoding/json" "strings" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/pkg/print" + "github.com/segmentio/terraform-docs/pkg/tfconf" ) -// Print prints a document as json. -func Print(module *tfconf.Module, settings *print.Settings) (string, error) { - module.Sort(settings) +// JSON represents JSON format. +type JSON struct{} +// NewJSON returns new instance of JSON. +func NewJSON(settings *print.Settings) *JSON { + return &JSON{} +} + +// Print prints a Terraform module as json. +func (j *JSON) Print(module *tfconf.Module, settings *print.Settings) (string, error) { copy := &tfconf.Module{ Header: "", Providers: make([]*tfconf.Provider, 0), diff --git a/internal/pkg/print/json/json_test.go b/internal/format/json_test.go similarity index 55% rename from internal/pkg/print/json/json_test.go rename to internal/format/json_test.go index 82b5c4a..b324961 100644 --- a/internal/pkg/print/json/json_test.go +++ b/internal/format/json_test.go @@ -1,11 +1,11 @@ -package json +package format import ( "testing" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/testutil" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/internal/module" + "github.com/segmentio/terraform-docs/internal/testutil" + "github.com/segmentio/terraform-docs/pkg/print" "github.com/stretchr/testify/assert" ) @@ -13,13 +13,15 @@ func TestJson(t *testing.T) { assert := assert.New(t) settings := testutil.Settings().WithSections().Build() - expected, err := testutil.GetExpected("json") + expected, err := testutil.GetExpected("json", "json") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -31,13 +33,19 @@ func TestJsonSortByName(t *testing.T) { SortByName: true, }).Build() - expected, err := testutil.GetExpected("json-SortByName") + expected, err := testutil.GetExpected("json", "json-SortByName") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions().With(&module.Options{ + SortBy: &module.SortBy{ + Name: true, + }, + }) + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -50,13 +58,20 @@ func TestJsonSortByRequired(t *testing.T) { SortByRequired: true, }).Build() - expected, err := testutil.GetExpected("json-SortByRequired") + expected, err := testutil.GetExpected("json", "json-SortByRequired") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions().With(&module.Options{ + SortBy: &module.SortBy{ + Name: true, + Required: true, + }, + }) + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -71,13 +86,15 @@ func TestJsonNoHeader(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("json-NoHeader") + expected, err := testutil.GetExpected("json", "json-NoHeader") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -92,13 +109,15 @@ func TestJsonNoProviders(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("json-NoProviders") + expected, err := testutil.GetExpected("json", "json-NoProviders") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -113,13 +132,15 @@ func TestJsonNoInputs(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("json-NoInputs") + expected, err := testutil.GetExpected("json", "json-NoInputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -134,13 +155,15 @@ func TestJsonNoOutputs(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("json-NoOutputs") + expected, err := testutil.GetExpected("json", "json-NoOutputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -155,13 +178,15 @@ func TestJsonOnlyHeader(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("json-OnlyHeader") + expected, err := testutil.GetExpected("json", "json-OnlyHeader") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -176,13 +201,15 @@ func TestJsonOnlyProviders(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("json-OnlyProviders") + expected, err := testutil.GetExpected("json", "json-OnlyProviders") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -197,13 +224,15 @@ func TestJsonOnlyInputs(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("json-OnlyInputs") + expected, err := testutil.GetExpected("json", "json-OnlyInputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -218,13 +247,15 @@ func TestJsonOnlyOutputs(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("json-OnlyOutputs") + expected, err := testutil.GetExpected("json", "json-OnlyOutputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -236,13 +267,15 @@ func TestJsonEscapeCharacters(t *testing.T) { EscapeCharacters: true, }).Build() - expected, err := testutil.GetExpected("json-EscapeCharacters") + expected, err := testutil.GetExpected("json", "json-EscapeCharacters") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -253,17 +286,18 @@ func TestJsonOutputValues(t *testing.T) { OutputValues: true, }).Build() - expected, err := testutil.GetExpected("json-OutputValues") + expected, err := testutil.GetExpected("json", "json-OutputValues") assert.Nil(err) - options := &tfconf.Options{ + options := module.NewOptions().With(&module.Options{ OutputValues: true, OutputValuesPath: "output_values.json", - } + }) module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewJSON(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/pkg/print/pretty/pretty.go b/internal/format/pretty.go similarity index 68% rename from internal/pkg/print/pretty/pretty.go rename to internal/format/pretty.go index ff9f296..1e93d8b 100644 --- a/internal/pkg/print/pretty/pretty.go +++ b/internal/format/pretty.go @@ -1,16 +1,16 @@ -package pretty +package format import ( "fmt" "text/template" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" - "github.com/segmentio/terraform-docs/internal/pkg/tmpl" + "github.com/segmentio/terraform-docs/pkg/print" + "github.com/segmentio/terraform-docs/pkg/tfconf" + "github.com/segmentio/terraform-docs/pkg/tmpl" ) const ( - headerTpl = ` + prettyHeaderTpl = ` {{- if .Settings.ShowHeader -}} {{- with .Module.Header }} {{- printf "\n" }} @@ -20,7 +20,7 @@ const ( {{ end -}} ` - providersTpl = ` + prettyProvidersTpl = ` {{- if .Settings.ShowProviders -}} {{- with .Module.Providers }} {{- printf "\n" -}} @@ -33,7 +33,7 @@ const ( {{ end -}} ` - inputsTpl = ` + prettyInputsTpl = ` {{- if .Settings.ShowInputs -}} {{- with .Module.Inputs }} {{- printf "\n" -}} @@ -46,7 +46,7 @@ const ( {{ end -}} ` - outputsTpl = ` + prettyOutputsTpl = ` {{- if .Settings.ShowOutputs -}} {{- with .Module.Outputs }} {{- printf "\n" -}} @@ -70,28 +70,31 @@ const ( ` ) -// Print prints a pretty document. -func Print(module *tfconf.Module, settings *print.Settings) (string, error) { - module.Sort(settings) +// Pretty represents colorized pretty format. +type Pretty struct { + template *tmpl.Template +} - t := tmpl.NewTemplate(&tmpl.Item{ +// NewPretty returns new instance of Pretty. +func NewPretty(settings *print.Settings) *Pretty { + tt := tmpl.NewTemplate(&tmpl.Item{ Name: "pretty", Text: prettyTpl, }, &tmpl.Item{ Name: "header", - Text: headerTpl, + Text: prettyHeaderTpl, }, &tmpl.Item{ Name: "providers", - Text: providersTpl, + Text: prettyProvidersTpl, }, &tmpl.Item{ Name: "inputs", - Text: inputsTpl, + Text: prettyInputsTpl, }, &tmpl.Item{ Name: "outputs", - Text: outputsTpl, + Text: prettyOutputsTpl, }) - t.Settings(settings) - t.CustomFunc(template.FuncMap{ + tt.Settings(settings) + tt.CustomFunc(template.FuncMap{ "colorize": func(c string, s string) string { r := "\033[0m" if !settings.ShowColor { @@ -101,10 +104,16 @@ func Print(module *tfconf.Module, settings *print.Settings) (string, error) { return fmt.Sprintf("%s%s%s", c, s, r) }, }) - rendered, err := t.Render(module) + return &Pretty{ + template: tt, + } +} + +// Print prints a Terraform module document. +func (p *Pretty) Print(module *tfconf.Module, settings *print.Settings) (string, error) { + rendered, err := p.template.Render(module) if err != nil { return "", err } - return rendered, nil } diff --git a/internal/pkg/print/pretty/pretty_test.go b/internal/format/pretty_test.go similarity index 55% rename from internal/pkg/print/pretty/pretty_test.go rename to internal/format/pretty_test.go index b76ae9d..21b2b26 100644 --- a/internal/pkg/print/pretty/pretty_test.go +++ b/internal/format/pretty_test.go @@ -1,11 +1,11 @@ -package pretty +package format import ( "testing" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/testutil" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/internal/module" + "github.com/segmentio/terraform-docs/internal/testutil" + "github.com/segmentio/terraform-docs/pkg/print" "github.com/stretchr/testify/assert" ) @@ -13,13 +13,15 @@ func TestPretty(t *testing.T) { assert := assert.New(t) settings := testutil.Settings().WithSections().WithColor().Build() - expected, err := testutil.GetExpected("pretty") + expected, err := testutil.GetExpected("pretty", "pretty") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -31,13 +33,19 @@ func TestPrettySortByName(t *testing.T) { SortByName: true, }).Build() - expected, err := testutil.GetExpected("pretty-SortByName") + expected, err := testutil.GetExpected("pretty", "pretty-SortByName") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions().With(&module.Options{ + SortBy: &module.SortBy{ + Name: true, + }, + }) + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -50,13 +58,20 @@ func TestPrettySortByRequired(t *testing.T) { SortByRequired: true, }).Build() - expected, err := testutil.GetExpected("pretty-SortByRequired") + expected, err := testutil.GetExpected("pretty", "pretty-SortByRequired") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions().With(&module.Options{ + SortBy: &module.SortBy{ + Name: true, + Required: true, + }, + }) + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -71,13 +86,15 @@ func TestPrettyNoHeader(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("pretty-NoHeader") + expected, err := testutil.GetExpected("pretty", "pretty-NoHeader") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -92,13 +109,15 @@ func TestPrettyNoProviders(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("pretty-NoProviders") + expected, err := testutil.GetExpected("pretty", "pretty-NoProviders") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -113,13 +132,15 @@ func TestPrettyNoInputs(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("pretty-NoInputs") + expected, err := testutil.GetExpected("pretty", "pretty-NoInputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -134,13 +155,15 @@ func TestPrettyNoOutputs(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("pretty-NoOutputs") + expected, err := testutil.GetExpected("pretty", "pretty-NoOutputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -155,13 +178,15 @@ func TestPrettyOnlyHeader(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("pretty-OnlyHeader") + expected, err := testutil.GetExpected("pretty", "pretty-OnlyHeader") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -176,13 +201,15 @@ func TestPrettyOnlyProviders(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("pretty-OnlyProviders") + expected, err := testutil.GetExpected("pretty", "pretty-OnlyProviders") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -197,13 +224,15 @@ func TestPrettyOnlyInputs(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("pretty-OnlyInputs") + expected, err := testutil.GetExpected("pretty", "pretty-OnlyInputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -218,13 +247,15 @@ func TestPrettyOnlyOutputs(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("pretty-OnlyOutputs") + expected, err := testutil.GetExpected("pretty", "pretty-OnlyOutputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -236,13 +267,15 @@ func TestPrettyNoColor(t *testing.T) { ShowColor: false, }).Build() - expected, err := testutil.GetExpected("pretty-NoColor") + expected, err := testutil.GetExpected("pretty", "pretty-NoColor") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -254,17 +287,18 @@ func TestPrettyOutputValues(t *testing.T) { OutputValues: true, }).Build() - expected, err := testutil.GetExpected("pretty-OutputValues") + expected, err := testutil.GetExpected("pretty", "pretty-OutputValues") assert.Nil(err) - options := &tfconf.Options{ + options := module.NewOptions().With(&module.Options{ OutputValues: true, OutputValuesPath: "output_values.json", - } + }) module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewPretty(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/pkg/print/markdown/table/table.go b/internal/format/table.go similarity index 71% rename from internal/pkg/print/markdown/table/table.go rename to internal/format/table.go index e9f0977..8c2de19 100644 --- a/internal/pkg/print/markdown/table/table.go +++ b/internal/format/table.go @@ -1,16 +1,15 @@ -package table +package format import ( "text/template" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/print/markdown" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" - "github.com/segmentio/terraform-docs/internal/pkg/tmpl" + "github.com/segmentio/terraform-docs/pkg/print" + "github.com/segmentio/terraform-docs/pkg/tfconf" + "github.com/segmentio/terraform-docs/pkg/tmpl" ) const ( - headerTpl = ` + tableHeaderTpl = ` {{- if .Settings.ShowHeader -}} {{- with .Module.Header -}} {{ sanitizeHeader . }} @@ -19,7 +18,7 @@ const ( {{ end -}} ` - providersTpl = ` + tableProvidersTpl = ` {{- if .Settings.ShowProviders -}} {{ indent 0 }} Providers {{ if not .Module.Providers }} @@ -34,7 +33,7 @@ const ( {{ end -}} ` - inputsTpl = ` + tableInputsTpl = ` {{- if .Settings.ShowInputs -}} {{ indent 0 }} Inputs {{ if not .Module.Inputs }} @@ -58,7 +57,7 @@ const ( {{ end -}} ` - outputsTpl = ` + tableOutputsTpl = ` {{- if .Settings.ShowOutputs -}} {{ indent 0 }} Outputs {{ if not .Module.Outputs }} @@ -81,44 +80,53 @@ const ( ` ) -// Print prints a document as Markdown tables. -func Print(module *tfconf.Module, settings *print.Settings) (string, error) { - module.Sort(settings) +// Table represents Markdown Table format. +type Table struct { + template *tmpl.Template +} - t := tmpl.NewTemplate(&tmpl.Item{ +// NewTable returns new instance of Table. +func NewTable(settings *print.Settings) *Table { + tt := tmpl.NewTemplate(&tmpl.Item{ Name: "table", Text: tableTpl, }, &tmpl.Item{ Name: "header", - Text: headerTpl, + Text: tableHeaderTpl, }, &tmpl.Item{ Name: "providers", - Text: providersTpl, + Text: tableProvidersTpl, }, &tmpl.Item{ Name: "inputs", - Text: inputsTpl, + Text: tableInputsTpl, }, &tmpl.Item{ Name: "outputs", - Text: outputsTpl, + Text: tableOutputsTpl, }) - t.Settings(settings) - t.CustomFunc(template.FuncMap{ + tt.Settings(settings) + tt.CustomFunc(template.FuncMap{ "type": func(t string) string { - inputType, _ := markdown.PrintFencedCodeBlock(t, "") + inputType, _ := printFencedCodeBlock(t, "") return inputType }, "value": func(v string) string { var result = "n/a" if v != "" { - result, _ = markdown.PrintFencedCodeBlock(v, "") + result, _ = printFencedCodeBlock(v, "") } return result }, }) - rendered, err := t.Render(module) + return &Table{ + template: tt, + } +} + +// Print prints a Terraform module as Markdown tables. +func (t *Table) Print(module *tfconf.Module, settings *print.Settings) (string, error) { + rendered, err := t.template.Render(module) if err != nil { return "", err } - - return markdown.Sanitize(rendered), nil + return sanitize(rendered), nil } diff --git a/internal/pkg/print/markdown/table/table_test.go b/internal/format/table_test.go similarity index 54% rename from internal/pkg/print/markdown/table/table_test.go rename to internal/format/table_test.go index d105dbe..91aca4b 100644 --- a/internal/pkg/print/markdown/table/table_test.go +++ b/internal/format/table_test.go @@ -1,11 +1,11 @@ -package table +package format import ( "testing" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/testutil" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/internal/module" + "github.com/segmentio/terraform-docs/internal/testutil" + "github.com/segmentio/terraform-docs/pkg/print" "github.com/stretchr/testify/assert" ) @@ -13,13 +13,15 @@ func TestTable(t *testing.T) { assert := assert.New(t) settings := testutil.Settings().WithSections().Build() - expected, err := testutil.GetExpected("table") + expected, err := testutil.GetExpected("table", "table") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -31,13 +33,15 @@ func TestTableWithRequired(t *testing.T) { ShowRequired: true, }).Build() - expected, err := testutil.GetExpected("table-WithRequired") + expected, err := testutil.GetExpected("table", "table-WithRequired") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -49,13 +53,19 @@ func TestTableSortByName(t *testing.T) { SortByName: true, }).Build() - expected, err := testutil.GetExpected("table-SortByName") + expected, err := testutil.GetExpected("table", "table-SortByName") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions().With(&module.Options{ + SortBy: &module.SortBy{ + Name: true, + }, + }) + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -68,13 +78,20 @@ func TestTableSortByRequired(t *testing.T) { SortByRequired: true, }).Build() - expected, err := testutil.GetExpected("table-SortByRequired") + expected, err := testutil.GetExpected("table", "table-SortByRequired") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions().With(&module.Options{ + SortBy: &module.SortBy{ + Name: true, + Required: true, + }, + }) + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -89,13 +106,15 @@ func TestTableNoHeader(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("table-NoHeader") + expected, err := testutil.GetExpected("table", "table-NoHeader") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -110,13 +129,15 @@ func TestTableNoProviders(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("table-NoProviders") + expected, err := testutil.GetExpected("table", "table-NoProviders") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -131,13 +152,15 @@ func TestTableNoInputs(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("table-NoInputs") + expected, err := testutil.GetExpected("table", "table-NoInputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -152,13 +175,15 @@ func TestTableNoOutputs(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("table-NoOutputs") + expected, err := testutil.GetExpected("table", "table-NoOutputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -173,13 +198,15 @@ func TestTableOnlyHeader(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("table-OnlyHeader") + expected, err := testutil.GetExpected("table", "table-OnlyHeader") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -194,13 +221,15 @@ func TestTableOnlyProviders(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("table-OnlyProviders") + expected, err := testutil.GetExpected("table", "table-OnlyProviders") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -215,13 +244,15 @@ func TestTableOnlyInputs(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("table-OnlyInputs") + expected, err := testutil.GetExpected("table", "table-OnlyInputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -236,13 +267,15 @@ func TestTableOnlyOutputs(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("table-OnlyOutputs") + expected, err := testutil.GetExpected("table", "table-OnlyOutputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -254,31 +287,35 @@ func TestTableEscapeCharacters(t *testing.T) { EscapeCharacters: true, }).Build() - expected, err := testutil.GetExpected("table-EscapeCharacters") + expected, err := testutil.GetExpected("table", "table-EscapeCharacters") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) } -func TestTableIndentationBellowAllowed(t *testing.T) { +func TestTableIndentationBelowAllowed(t *testing.T) { assert := assert.New(t) settings := testutil.Settings().WithSections().With(&print.Settings{ MarkdownIndent: 0, }).Build() - expected, err := testutil.GetExpected("table-IndentationBellowAllowed") + expected, err := testutil.GetExpected("table", "table-IndentationBelowAllowed") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -290,13 +327,15 @@ func TestTableIndentationAboveAllowed(t *testing.T) { MarkdownIndent: 10, }).Build() - expected, err := testutil.GetExpected("table-IndentationAboveAllowed") + expected, err := testutil.GetExpected("table", "table-IndentationAboveAllowed") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -308,13 +347,15 @@ func TestTableIndentationOfFour(t *testing.T) { MarkdownIndent: 4, }).Build() - expected, err := testutil.GetExpected("table-IndentationOfFour") + expected, err := testutil.GetExpected("table", "table-IndentationOfFour") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -326,17 +367,18 @@ func TestTableOutputValues(t *testing.T) { OutputValues: true, }).Build() - expected, err := testutil.GetExpected("table-OutputValues") + expected, err := testutil.GetExpected("table", "table-OutputValues") assert.Nil(err) - options := &tfconf.Options{ + options := module.NewOptions().With(&module.Options{ OutputValues: true, OutputValuesPath: "output_values.json", - } + }) module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewTable(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/pkg/print/markdown/document/testdata/document-EscapeCharacters.golden b/internal/format/testdata/document/document-EscapeCharacters.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-EscapeCharacters.golden rename to internal/format/testdata/document/document-EscapeCharacters.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-IndentationAboveAllowed.golden b/internal/format/testdata/document/document-IndentationAboveAllowed.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-IndentationAboveAllowed.golden rename to internal/format/testdata/document/document-IndentationAboveAllowed.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-IndentationBelowAllowed.golden b/internal/format/testdata/document/document-IndentationBelowAllowed.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-IndentationBelowAllowed.golden rename to internal/format/testdata/document/document-IndentationBelowAllowed.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-IndentationOfFour.golden b/internal/format/testdata/document/document-IndentationOfFour.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-IndentationOfFour.golden rename to internal/format/testdata/document/document-IndentationOfFour.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-NoHeader.golden b/internal/format/testdata/document/document-NoHeader.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-NoHeader.golden rename to internal/format/testdata/document/document-NoHeader.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-NoInputs.golden b/internal/format/testdata/document/document-NoInputs.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-NoInputs.golden rename to internal/format/testdata/document/document-NoInputs.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-NoOutputs.golden b/internal/format/testdata/document/document-NoOutputs.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-NoOutputs.golden rename to internal/format/testdata/document/document-NoOutputs.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-NoProviders.golden b/internal/format/testdata/document/document-NoProviders.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-NoProviders.golden rename to internal/format/testdata/document/document-NoProviders.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-OnlyHeader.golden b/internal/format/testdata/document/document-OnlyHeader.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-OnlyHeader.golden rename to internal/format/testdata/document/document-OnlyHeader.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-OnlyInputs.golden b/internal/format/testdata/document/document-OnlyInputs.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-OnlyInputs.golden rename to internal/format/testdata/document/document-OnlyInputs.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-OnlyOutputs.golden b/internal/format/testdata/document/document-OnlyOutputs.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-OnlyOutputs.golden rename to internal/format/testdata/document/document-OnlyOutputs.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-OnlyProviders.golden b/internal/format/testdata/document/document-OnlyProviders.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-OnlyProviders.golden rename to internal/format/testdata/document/document-OnlyProviders.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-OutputValues.golden b/internal/format/testdata/document/document-OutputValues.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-OutputValues.golden rename to internal/format/testdata/document/document-OutputValues.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-SortByName.golden b/internal/format/testdata/document/document-SortByName.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-SortByName.golden rename to internal/format/testdata/document/document-SortByName.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-SortByRequired.golden b/internal/format/testdata/document/document-SortByRequired.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-SortByRequired.golden rename to internal/format/testdata/document/document-SortByRequired.golden diff --git a/internal/pkg/print/markdown/document/testdata/document-WithRequired.golden b/internal/format/testdata/document/document-WithRequired.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document-WithRequired.golden rename to internal/format/testdata/document/document-WithRequired.golden diff --git a/internal/pkg/print/markdown/document/testdata/document.golden b/internal/format/testdata/document/document.golden similarity index 100% rename from internal/pkg/print/markdown/document/testdata/document.golden rename to internal/format/testdata/document/document.golden diff --git a/internal/pkg/print/json/testdata/json-EscapeCharacters.golden b/internal/format/testdata/json/json-EscapeCharacters.golden similarity index 100% rename from internal/pkg/print/json/testdata/json-EscapeCharacters.golden rename to internal/format/testdata/json/json-EscapeCharacters.golden diff --git a/internal/pkg/print/json/testdata/json-NoHeader.golden b/internal/format/testdata/json/json-NoHeader.golden similarity index 100% rename from internal/pkg/print/json/testdata/json-NoHeader.golden rename to internal/format/testdata/json/json-NoHeader.golden diff --git a/internal/pkg/print/json/testdata/json-NoInputs.golden b/internal/format/testdata/json/json-NoInputs.golden similarity index 100% rename from internal/pkg/print/json/testdata/json-NoInputs.golden rename to internal/format/testdata/json/json-NoInputs.golden diff --git a/internal/pkg/print/json/testdata/json-NoOutputs.golden b/internal/format/testdata/json/json-NoOutputs.golden similarity index 100% rename from internal/pkg/print/json/testdata/json-NoOutputs.golden rename to internal/format/testdata/json/json-NoOutputs.golden diff --git a/internal/pkg/print/json/testdata/json-NoProviders.golden b/internal/format/testdata/json/json-NoProviders.golden similarity index 100% rename from internal/pkg/print/json/testdata/json-NoProviders.golden rename to internal/format/testdata/json/json-NoProviders.golden diff --git a/internal/pkg/print/json/testdata/json-OnlyHeader.golden b/internal/format/testdata/json/json-OnlyHeader.golden similarity index 100% rename from internal/pkg/print/json/testdata/json-OnlyHeader.golden rename to internal/format/testdata/json/json-OnlyHeader.golden diff --git a/internal/pkg/print/json/testdata/json-OnlyInputs.golden b/internal/format/testdata/json/json-OnlyInputs.golden similarity index 100% rename from internal/pkg/print/json/testdata/json-OnlyInputs.golden rename to internal/format/testdata/json/json-OnlyInputs.golden diff --git a/internal/pkg/print/json/testdata/json-OnlyOutputs.golden b/internal/format/testdata/json/json-OnlyOutputs.golden similarity index 100% rename from internal/pkg/print/json/testdata/json-OnlyOutputs.golden rename to internal/format/testdata/json/json-OnlyOutputs.golden diff --git a/internal/pkg/print/json/testdata/json-OnlyProviders.golden b/internal/format/testdata/json/json-OnlyProviders.golden similarity index 100% rename from internal/pkg/print/json/testdata/json-OnlyProviders.golden rename to internal/format/testdata/json/json-OnlyProviders.golden diff --git a/internal/pkg/print/json/testdata/json-OutputValues.golden b/internal/format/testdata/json/json-OutputValues.golden similarity index 100% rename from internal/pkg/print/json/testdata/json-OutputValues.golden rename to internal/format/testdata/json/json-OutputValues.golden diff --git a/internal/pkg/print/json/testdata/json-SortByName.golden b/internal/format/testdata/json/json-SortByName.golden similarity index 100% rename from internal/pkg/print/json/testdata/json-SortByName.golden rename to internal/format/testdata/json/json-SortByName.golden diff --git a/internal/pkg/print/json/testdata/json-SortByRequired.golden b/internal/format/testdata/json/json-SortByRequired.golden similarity index 100% rename from internal/pkg/print/json/testdata/json-SortByRequired.golden rename to internal/format/testdata/json/json-SortByRequired.golden diff --git a/internal/pkg/print/json/testdata/json.golden b/internal/format/testdata/json/json.golden similarity index 100% rename from internal/pkg/print/json/testdata/json.golden rename to internal/format/testdata/json/json.golden diff --git a/internal/pkg/print/pretty/testdata/pretty-NoColor.golden b/internal/format/testdata/pretty/pretty-NoColor.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty-NoColor.golden rename to internal/format/testdata/pretty/pretty-NoColor.golden diff --git a/internal/pkg/print/pretty/testdata/pretty-NoHeader.golden b/internal/format/testdata/pretty/pretty-NoHeader.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty-NoHeader.golden rename to internal/format/testdata/pretty/pretty-NoHeader.golden diff --git a/internal/pkg/print/pretty/testdata/pretty-NoInputs.golden b/internal/format/testdata/pretty/pretty-NoInputs.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty-NoInputs.golden rename to internal/format/testdata/pretty/pretty-NoInputs.golden diff --git a/internal/pkg/print/pretty/testdata/pretty-NoOutputs.golden b/internal/format/testdata/pretty/pretty-NoOutputs.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty-NoOutputs.golden rename to internal/format/testdata/pretty/pretty-NoOutputs.golden diff --git a/internal/pkg/print/pretty/testdata/pretty-NoProviders.golden b/internal/format/testdata/pretty/pretty-NoProviders.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty-NoProviders.golden rename to internal/format/testdata/pretty/pretty-NoProviders.golden diff --git a/internal/pkg/print/pretty/testdata/pretty-OnlyHeader.golden b/internal/format/testdata/pretty/pretty-OnlyHeader.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty-OnlyHeader.golden rename to internal/format/testdata/pretty/pretty-OnlyHeader.golden diff --git a/internal/pkg/print/pretty/testdata/pretty-OnlyInputs.golden b/internal/format/testdata/pretty/pretty-OnlyInputs.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty-OnlyInputs.golden rename to internal/format/testdata/pretty/pretty-OnlyInputs.golden diff --git a/internal/pkg/print/pretty/testdata/pretty-OnlyOutputs.golden b/internal/format/testdata/pretty/pretty-OnlyOutputs.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty-OnlyOutputs.golden rename to internal/format/testdata/pretty/pretty-OnlyOutputs.golden diff --git a/internal/pkg/print/pretty/testdata/pretty-OnlyProviders.golden b/internal/format/testdata/pretty/pretty-OnlyProviders.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty-OnlyProviders.golden rename to internal/format/testdata/pretty/pretty-OnlyProviders.golden diff --git a/internal/pkg/print/pretty/testdata/pretty-OutputValues.golden b/internal/format/testdata/pretty/pretty-OutputValues.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty-OutputValues.golden rename to internal/format/testdata/pretty/pretty-OutputValues.golden diff --git a/internal/pkg/print/pretty/testdata/pretty-SortByName.golden b/internal/format/testdata/pretty/pretty-SortByName.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty-SortByName.golden rename to internal/format/testdata/pretty/pretty-SortByName.golden diff --git a/internal/pkg/print/pretty/testdata/pretty-SortByRequired.golden b/internal/format/testdata/pretty/pretty-SortByRequired.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty-SortByRequired.golden rename to internal/format/testdata/pretty/pretty-SortByRequired.golden diff --git a/internal/pkg/print/pretty/testdata/pretty.golden b/internal/format/testdata/pretty/pretty.golden similarity index 100% rename from internal/pkg/print/pretty/testdata/pretty.golden rename to internal/format/testdata/pretty/pretty.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-EscapeCharacters.golden b/internal/format/testdata/table/table-EscapeCharacters.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-EscapeCharacters.golden rename to internal/format/testdata/table/table-EscapeCharacters.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-IndentationAboveAllowed.golden b/internal/format/testdata/table/table-IndentationAboveAllowed.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-IndentationAboveAllowed.golden rename to internal/format/testdata/table/table-IndentationAboveAllowed.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-IndentationBellowAllowed.golden b/internal/format/testdata/table/table-IndentationBelowAllowed.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-IndentationBellowAllowed.golden rename to internal/format/testdata/table/table-IndentationBelowAllowed.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-IndentationOfFour.golden b/internal/format/testdata/table/table-IndentationOfFour.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-IndentationOfFour.golden rename to internal/format/testdata/table/table-IndentationOfFour.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-NoHeader.golden b/internal/format/testdata/table/table-NoHeader.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-NoHeader.golden rename to internal/format/testdata/table/table-NoHeader.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-NoInputs.golden b/internal/format/testdata/table/table-NoInputs.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-NoInputs.golden rename to internal/format/testdata/table/table-NoInputs.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-NoOutputs.golden b/internal/format/testdata/table/table-NoOutputs.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-NoOutputs.golden rename to internal/format/testdata/table/table-NoOutputs.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-NoProviders.golden b/internal/format/testdata/table/table-NoProviders.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-NoProviders.golden rename to internal/format/testdata/table/table-NoProviders.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-OnlyHeader.golden b/internal/format/testdata/table/table-OnlyHeader.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-OnlyHeader.golden rename to internal/format/testdata/table/table-OnlyHeader.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-OnlyInputs.golden b/internal/format/testdata/table/table-OnlyInputs.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-OnlyInputs.golden rename to internal/format/testdata/table/table-OnlyInputs.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-OnlyOutputs.golden b/internal/format/testdata/table/table-OnlyOutputs.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-OnlyOutputs.golden rename to internal/format/testdata/table/table-OnlyOutputs.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-OnlyProviders.golden b/internal/format/testdata/table/table-OnlyProviders.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-OnlyProviders.golden rename to internal/format/testdata/table/table-OnlyProviders.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-OutputValues.golden b/internal/format/testdata/table/table-OutputValues.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-OutputValues.golden rename to internal/format/testdata/table/table-OutputValues.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-SortByName.golden b/internal/format/testdata/table/table-SortByName.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-SortByName.golden rename to internal/format/testdata/table/table-SortByName.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-SortByRequired.golden b/internal/format/testdata/table/table-SortByRequired.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-SortByRequired.golden rename to internal/format/testdata/table/table-SortByRequired.golden diff --git a/internal/pkg/print/markdown/table/testdata/table-WithRequired.golden b/internal/format/testdata/table/table-WithRequired.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table-WithRequired.golden rename to internal/format/testdata/table/table-WithRequired.golden diff --git a/internal/pkg/print/markdown/table/testdata/table.golden b/internal/format/testdata/table/table.golden similarity index 100% rename from internal/pkg/print/markdown/table/testdata/table.golden rename to internal/format/testdata/table/table.golden diff --git a/internal/pkg/print/yaml/testdata/yaml-NoHeader.golden b/internal/format/testdata/yaml/yaml-NoHeader.golden similarity index 100% rename from internal/pkg/print/yaml/testdata/yaml-NoHeader.golden rename to internal/format/testdata/yaml/yaml-NoHeader.golden diff --git a/internal/pkg/print/yaml/testdata/yaml-NoInputs.golden b/internal/format/testdata/yaml/yaml-NoInputs.golden similarity index 100% rename from internal/pkg/print/yaml/testdata/yaml-NoInputs.golden rename to internal/format/testdata/yaml/yaml-NoInputs.golden diff --git a/internal/pkg/print/yaml/testdata/yaml-NoOutputs.golden b/internal/format/testdata/yaml/yaml-NoOutputs.golden similarity index 100% rename from internal/pkg/print/yaml/testdata/yaml-NoOutputs.golden rename to internal/format/testdata/yaml/yaml-NoOutputs.golden diff --git a/internal/pkg/print/yaml/testdata/yaml-NoProviders.golden b/internal/format/testdata/yaml/yaml-NoProviders.golden similarity index 100% rename from internal/pkg/print/yaml/testdata/yaml-NoProviders.golden rename to internal/format/testdata/yaml/yaml-NoProviders.golden diff --git a/internal/pkg/print/yaml/testdata/yaml-OnlyHeader.golden b/internal/format/testdata/yaml/yaml-OnlyHeader.golden similarity index 100% rename from internal/pkg/print/yaml/testdata/yaml-OnlyHeader.golden rename to internal/format/testdata/yaml/yaml-OnlyHeader.golden diff --git a/internal/pkg/print/yaml/testdata/yaml-OnlyInputs.golden b/internal/format/testdata/yaml/yaml-OnlyInputs.golden similarity index 100% rename from internal/pkg/print/yaml/testdata/yaml-OnlyInputs.golden rename to internal/format/testdata/yaml/yaml-OnlyInputs.golden diff --git a/internal/pkg/print/yaml/testdata/yaml-OnlyOutputs.golden b/internal/format/testdata/yaml/yaml-OnlyOutputs.golden similarity index 100% rename from internal/pkg/print/yaml/testdata/yaml-OnlyOutputs.golden rename to internal/format/testdata/yaml/yaml-OnlyOutputs.golden diff --git a/internal/pkg/print/yaml/testdata/yaml-OnlyProviders.golden b/internal/format/testdata/yaml/yaml-OnlyProviders.golden similarity index 100% rename from internal/pkg/print/yaml/testdata/yaml-OnlyProviders.golden rename to internal/format/testdata/yaml/yaml-OnlyProviders.golden diff --git a/internal/pkg/print/yaml/testdata/yaml-OutputValues.golden b/internal/format/testdata/yaml/yaml-OutputValues.golden similarity index 100% rename from internal/pkg/print/yaml/testdata/yaml-OutputValues.golden rename to internal/format/testdata/yaml/yaml-OutputValues.golden diff --git a/internal/pkg/print/yaml/testdata/yaml-SortByName.golden b/internal/format/testdata/yaml/yaml-SortByName.golden similarity index 100% rename from internal/pkg/print/yaml/testdata/yaml-SortByName.golden rename to internal/format/testdata/yaml/yaml-SortByName.golden diff --git a/internal/pkg/print/yaml/testdata/yaml-SortByRequired.golden b/internal/format/testdata/yaml/yaml-SortByRequired.golden similarity index 100% rename from internal/pkg/print/yaml/testdata/yaml-SortByRequired.golden rename to internal/format/testdata/yaml/yaml-SortByRequired.golden diff --git a/internal/pkg/print/yaml/testdata/yaml.golden b/internal/format/testdata/yaml/yaml.golden similarity index 100% rename from internal/pkg/print/yaml/testdata/yaml.golden rename to internal/format/testdata/yaml/yaml.golden diff --git a/internal/format/util.go b/internal/format/util.go new file mode 100644 index 0000000..455edf1 --- /dev/null +++ b/internal/format/util.go @@ -0,0 +1,43 @@ +package format + +import ( + "fmt" + "regexp" + "strings" +) + +// sanitize cleans a Markdown document to soothe linters. +func sanitize(markdown string) string { + result := markdown + + // Preserve double spaces at the end of the line + result = regexp.MustCompile(` {2}(\r?\n)`).ReplaceAllString(result, "‡‡$1") + + // Remove trailing spaces from the end of lines + result = regexp.MustCompile(` +(\r?\n)`).ReplaceAllString(result, "$1") + result = regexp.MustCompile(` +$`).ReplaceAllLiteralString(result, "") + + // Preserve double spaces at the end of the line + result = regexp.MustCompile(`‡‡(\r?\n)`).ReplaceAllString(result, " $1") + + // Remove blank line with only double spaces in it + result = regexp.MustCompile(`(\r?\n) (\r?\n)`).ReplaceAllString(result, "$1") + + // Remove multiple consecutive blank lines + result = regexp.MustCompile(`(\r?\n){3,}`).ReplaceAllString(result, "$1$1") + result = regexp.MustCompile(`(\r?\n){2,}$`).ReplaceAllString(result, "$1") + + return result +} + +// printFencedCodeBlock prints codes in fences, it automatically detects if +// the input 'code' contains '\n' it will use multi line fence, otherwise it +// wraps the 'code' inside single-tick block. +// If the fenced is multi-line it also appens an extra '\n` at the end and +// returns true accordingly, otherwise returns false for non-carriage return. +func printFencedCodeBlock(code string, language string) (string, bool) { + if strings.Contains(code, "\n") { + return fmt.Sprintf("\n\n```%s\n%s\n```\n", language, code), true + } + return fmt.Sprintf("`%s`", code), false +} diff --git a/internal/pkg/print/yaml/yaml.go b/internal/format/yaml.go similarity index 59% rename from internal/pkg/print/yaml/yaml.go rename to internal/format/yaml.go index 890607f..dba3ac0 100644 --- a/internal/pkg/print/yaml/yaml.go +++ b/internal/format/yaml.go @@ -1,17 +1,23 @@ -package yaml +package format import ( "strings" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/pkg/print" + "github.com/segmentio/terraform-docs/pkg/tfconf" "gopkg.in/yaml.v2" ) -// Print prints a document as yaml. -func Print(module *tfconf.Module, settings *print.Settings) (string, error) { - module.Sort(settings) +// YAML represents YAML format. +type YAML struct{} +// NewYAML returns new instance of YAML. +func NewYAML(settings *print.Settings) *YAML { + return &YAML{} +} + +// Print prints a Terraform module as yaml. +func (y *YAML) Print(module *tfconf.Module, settings *print.Settings) (string, error) { copy := &tfconf.Module{ Header: "", Providers: make([]*tfconf.Provider, 0), diff --git a/internal/pkg/print/yaml/yaml_test.go b/internal/format/yaml_test.go similarity index 55% rename from internal/pkg/print/yaml/yaml_test.go rename to internal/format/yaml_test.go index 2d0705d..229636c 100644 --- a/internal/pkg/print/yaml/yaml_test.go +++ b/internal/format/yaml_test.go @@ -1,11 +1,11 @@ -package yaml +package format import ( "testing" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/testutil" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/internal/module" + "github.com/segmentio/terraform-docs/internal/testutil" + "github.com/segmentio/terraform-docs/pkg/print" "github.com/stretchr/testify/assert" ) @@ -13,13 +13,15 @@ func TestYaml(t *testing.T) { assert := assert.New(t) settings := testutil.Settings().WithSections().Build() - expected, err := testutil.GetExpected("yaml") + expected, err := testutil.GetExpected("yaml", "yaml") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewYAML(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -31,13 +33,19 @@ func TestYamlSortByName(t *testing.T) { SortByName: true, }).Build() - expected, err := testutil.GetExpected("yaml-SortByName") + expected, err := testutil.GetExpected("yaml", "yaml-SortByName") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions().With(&module.Options{ + SortBy: &module.SortBy{ + Name: true, + }, + }) + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewYAML(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -50,13 +58,20 @@ func TestYamlSortByRequired(t *testing.T) { SortByRequired: true, }).Build() - expected, err := testutil.GetExpected("yaml-SortByRequired") + expected, err := testutil.GetExpected("yaml", "yaml-SortByRequired") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions().With(&module.Options{ + SortBy: &module.SortBy{ + Name: true, + Required: true, + }, + }) + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewYAML(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -71,13 +86,15 @@ func TestYamlNoHeader(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("yaml-NoHeader") + expected, err := testutil.GetExpected("yaml", "yaml-NoHeader") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewYAML(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -92,13 +109,15 @@ func TestYamlNoProviders(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("yaml-NoProviders") + expected, err := testutil.GetExpected("yaml", "yaml-NoProviders") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewYAML(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -113,13 +132,15 @@ func TestYamlNoInputs(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("yaml-NoInputs") + expected, err := testutil.GetExpected("yaml", "yaml-NoInputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewYAML(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -134,13 +155,15 @@ func TestYamlNoOutputs(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("yaml-NoOutputs") + expected, err := testutil.GetExpected("yaml", "yaml-NoOutputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewYAML(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -155,13 +178,15 @@ func TestYamlOnlyHeader(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("yaml-OnlyHeader") + expected, err := testutil.GetExpected("yaml", "yaml-OnlyHeader") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewYAML(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -176,13 +201,15 @@ func TestYamlOnlyProviders(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("yaml-OnlyProviders") + expected, err := testutil.GetExpected("yaml", "yaml-OnlyProviders") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewYAML(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -197,13 +224,15 @@ func TestYamlOnlyInputs(t *testing.T) { ShowOutputs: false, }).Build() - expected, err := testutil.GetExpected("yaml-OnlyInputs") + expected, err := testutil.GetExpected("yaml", "yaml-OnlyInputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewYAML(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -218,13 +247,15 @@ func TestYamlOnlyOutputs(t *testing.T) { ShowOutputs: true, }).Build() - expected, err := testutil.GetExpected("yaml-OnlyOutputs") + expected, err := testutil.GetExpected("yaml", "yaml-OnlyOutputs") assert.Nil(err) - module, err := testutil.GetModule(new(tfconf.Options)) + options := module.NewOptions() + module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewYAML(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) @@ -236,17 +267,18 @@ func TestYamlOutputValues(t *testing.T) { OutputValues: true, }).Build() - expected, err := testutil.GetExpected("yaml-OutputValues") + expected, err := testutil.GetExpected("yaml", "yaml-OutputValues") assert.Nil(err) - options := &tfconf.Options{ + options := module.NewOptions().With(&module.Options{ OutputValues: true, OutputValuesPath: "output_values.json", - } + }) module, err := testutil.GetModule(options) assert.Nil(err) - actual, err := Print(module, settings) + printer := NewYAML(settings) + actual, err := printer.Print(module, settings) assert.Nil(err) assert.Equal(expected, actual) diff --git a/internal/module/input.go b/internal/module/input.go new file mode 100644 index 0000000..f0b176c --- /dev/null +++ b/internal/module/input.go @@ -0,0 +1,39 @@ +package module + +import ( + "github.com/segmentio/terraform-docs/pkg/tfconf" +) + +type inputsSortedByName []*tfconf.Input + +func (a inputsSortedByName) Len() int { return len(a) } +func (a inputsSortedByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a inputsSortedByName) Less(i, j int) bool { + return a[i].Name < a[j].Name +} + +type inputsSortedByRequired []*tfconf.Input + +func (a inputsSortedByRequired) Len() int { return len(a) } +func (a inputsSortedByRequired) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a inputsSortedByRequired) Less(i, j int) bool { + switch { + // i required, j not: i gets priority + case !a[i].HasDefault() && a[j].HasDefault(): + return true + // j required, i not: i does not get priority + case a[i].HasDefault() && !a[j].HasDefault(): + return false + // Otherwise, sort by name + default: + return a[i].Name < a[j].Name + } +} + +type inputsSortedByPosition []*tfconf.Input + +func (a inputsSortedByPosition) Len() int { return len(a) } +func (a inputsSortedByPosition) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a inputsSortedByPosition) Less(i, j int) bool { + return a[i].Position.Filename < a[j].Position.Filename || a[i].Position.Line < a[j].Position.Line +} diff --git a/internal/module/module.go b/internal/module/module.go new file mode 100644 index 0000000..6ebe9f1 --- /dev/null +++ b/internal/module/module.go @@ -0,0 +1,264 @@ +package module + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os/exec" + "path/filepath" + "sort" + "strings" + + "github.com/hashicorp/terraform-config-inspect/tfconfig" + "github.com/segmentio/terraform-docs/internal/reader" + "github.com/segmentio/terraform-docs/internal/types" + "github.com/segmentio/terraform-docs/pkg/tfconf" +) + +// LoadWithOptions returns new instance of Module with all the inputs and +// outputs discovered from provided 'path' containing Terraform config +func LoadWithOptions(options *Options) (*tfconf.Module, error) { + tfmodule := loadModule(options.Path) + + header := loadHeader(options.Path) + inputs, required, optional := loadInputs(tfmodule) + outputs := loadOutputs(tfmodule, options) + providers := loadProviders(tfmodule) + + module := &tfconf.Module{ + Header: header, + Inputs: inputs, + Outputs: outputs, + Providers: providers, + + RequiredInputs: required, + OptionalInputs: optional, + } + sortItems(module, options.SortBy) + return module, nil +} + +func loadModule(path string) *tfconfig.Module { + module, diag := tfconfig.LoadModule(path) + if diag != nil && diag.HasErrors() { + log.Fatal(diag) + } + return module +} + +func loadHeader(path string) string { + filename := filepath.Join(path, "main.tf") + _, err := ioutil.ReadFile(filename) + if err != nil { + return "" // absorb the error, we don't need to bubble it up or break the execution + } + lines := reader.Lines{ + FileName: filename, + LineNum: -1, + Condition: func(line string) bool { + line = strings.TrimSpace(line) + return strings.HasPrefix(line, "/*") || strings.HasPrefix(line, "*") || strings.HasPrefix(line, "*/") + }, + Parser: func(line string) (string, bool) { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "/*") || strings.HasPrefix(line, "*/") { + return "", false + } + if line == "*" { + return "", true + } + line = strings.TrimPrefix(line, "* ") + return line, true + }, + } + header, err := lines.Extract() + if err != nil { + return "" // absorb the error, we don't need to bubble it up or break the execution + } + return strings.Join(header, "\n") +} + +func loadInputs(tfmodule *tfconfig.Module) ([]*tfconf.Input, []*tfconf.Input, []*tfconf.Input) { + var inputs = make([]*tfconf.Input, 0, len(tfmodule.Variables)) + var required = make([]*tfconf.Input, 0, len(tfmodule.Variables)) + var optional = make([]*tfconf.Input, 0, len(tfmodule.Variables)) + + for _, input := range tfmodule.Variables { + inputType := input.Type + if input.Type == "" { + inputType = "any" + if input.Default != nil { + switch xType := fmt.Sprintf("%T", input.Default); xType { + case "string": + inputType = "string" + case "int", "int8", "int16", "int32", "int64", "float32", "float64": + inputType = "number" + case "bool": + inputType = "bool" + case "[]interface {}": + inputType = "list" + case "map[string]interface {}": + inputType = "map" + } + } + } + + inputDescription := input.Description + if inputDescription == "" { + inputDescription = loadComments(input.Pos.Filename, input.Pos.Line-1) + } + + i := &tfconf.Input{ + Name: input.Name, + Type: types.String(inputType), + Description: types.String(inputDescription), + Default: input.Default, + Position: tfconf.Position{ + Filename: input.Pos.Filename, + Line: input.Pos.Line, + }, + } + + inputs = append(inputs, i) + if i.HasDefault() { + optional = append(optional, i) + } else { + required = append(required, i) + } + } + return inputs, required, optional +} + +func loadOutputs(tfmodule *tfconfig.Module, options *Options) []*tfconf.Output { + outputs := make([]*tfconf.Output, 0, len(tfmodule.Outputs)) + for _, o := range tfmodule.Outputs { + description := o.Description + if description == "" { + description = loadComments(o.Pos.Filename, o.Pos.Line-1) + } + output := &tfconf.Output{ + Name: o.Name, + Description: types.String(description), + Position: tfconf.Position{ + Filename: o.Pos.Filename, + Line: o.Pos.Line, + }, + } + if options.OutputValues { + terraformOutputs, err := loadOutputValues(options) + if err != nil { + log.Fatal(err) + } + if terraformOutputs[output.Name].Sensitive { + output.Value = "" + } else { + output.Value = terraformOutputs[output.Name].Value + } + } + outputs = append(outputs, output) + } + return outputs +} + +func loadOutputValues(options *Options) (map[string]*TerraformOutput, error) { + var out []byte + var err error + if options.OutputValuesPath == "" { + cmd := exec.Command("terraform", "output", "-json") + cmd.Dir = options.Path + if out, err = cmd.Output(); err != nil { + return nil, fmt.Errorf("caught error while reading the terraform outputs: %v", err) + } + } else { + if out, err = ioutil.ReadFile(options.OutputValuesPath); err != nil { + return nil, fmt.Errorf("caught error while reading the terraform outputs file at %s: %v", options.OutputValuesPath, err) + } + } + var terraformOutputs map[string]*TerraformOutput + err = json.Unmarshal(out, &terraformOutputs) + if err != nil { + return nil, err + } + return terraformOutputs, err +} + +func loadProviders(tfmodule *tfconfig.Module) []*tfconf.Provider { + resources := []map[string]*tfconfig.Resource{tfmodule.ManagedResources, tfmodule.DataResources} + discovered := make(map[string]*tfconf.Provider) + for _, resource := range resources { + for _, r := range resource { + var version = "" + if rv, ok := tfmodule.RequiredProviders[r.Provider.Name]; ok && len(rv.VersionConstraints) > 0 { + version = strings.Join(rv.VersionConstraints, " ") + } + key := fmt.Sprintf("%s.%s", r.Provider.Name, r.Provider.Alias) + discovered[key] = &tfconf.Provider{ + Name: r.Provider.Name, + Alias: types.String(r.Provider.Alias), + Version: types.String(version), + Position: tfconf.Position{ + Filename: r.Pos.Filename, + Line: r.Pos.Line, + }, + } + } + } + providers := make([]*tfconf.Provider, 0, len(discovered)) + for _, provider := range discovered { + providers = append(providers, provider) + } + return providers +} + +func loadComments(filename string, lineNum int) string { + lines := reader.Lines{ + FileName: filename, + LineNum: lineNum, + Condition: func(line string) bool { + return strings.HasPrefix(line, "#") || strings.HasPrefix(line, "//") + }, + Parser: func(line string) (string, bool) { + line = strings.TrimSpace(line) + line = strings.TrimPrefix(line, "#") + line = strings.TrimPrefix(line, "//") + line = strings.TrimSpace(line) + return line, true + }, + } + comment, err := lines.Extract() + if err != nil { + return "" // absorb the error, we don't need to bubble it up or break the execution + } + return strings.Join(comment, " ") +} + +func sortItems(tfmodule *tfconf.Module, sortby *SortBy) { + if sortby.Name { + sort.Sort(providersSortedByName(tfmodule.Providers)) + } else { + sort.Sort(providersSortedByPosition(tfmodule.Providers)) + } + + if sortby.Name { + if sortby.Required { + sort.Sort(inputsSortedByRequired(tfmodule.Inputs)) + sort.Sort(inputsSortedByRequired(tfmodule.RequiredInputs)) + sort.Sort(inputsSortedByRequired(tfmodule.OptionalInputs)) + } else { + sort.Sort(inputsSortedByName(tfmodule.Inputs)) + sort.Sort(inputsSortedByName(tfmodule.RequiredInputs)) + sort.Sort(inputsSortedByName(tfmodule.OptionalInputs)) + } + } else { + sort.Sort(inputsSortedByPosition(tfmodule.Inputs)) + sort.Sort(inputsSortedByPosition(tfmodule.RequiredInputs)) + sort.Sort(inputsSortedByPosition(tfmodule.OptionalInputs)) + } + + if sortby.Name { + sort.Sort(outputsSortedByName(tfmodule.Outputs)) + } else { + sort.Sort(outputsSortedByPosition(tfmodule.Outputs)) + } +} diff --git a/internal/module/options.go b/internal/module/options.go new file mode 100644 index 0000000..2412175 --- /dev/null +++ b/internal/module/options.go @@ -0,0 +1,40 @@ +package module + +import ( + "log" + + "github.com/imdario/mergo" +) + +// SortBy contains different sort criteria corresponding +// to available flags (e.g. name, required, etc) +type SortBy struct { + Name bool + Required bool +} + +// Options contains required options to load a Module from path +type Options struct { + Path string + SortBy *SortBy + OutputValues bool + OutputValuesPath string +} + +// NewOptions returns new instance of Options +func NewOptions() *Options { + return &Options{ + Path: "", + SortBy: &SortBy{Name: false, Required: false}, + OutputValues: false, + OutputValuesPath: "", + } +} + +// With override options with existing Options +func (o *Options) With(override *Options) *Options { + if err := mergo.Merge(o, override); err != nil { + log.Fatal(err) + } + return o +} diff --git a/internal/module/output.go b/internal/module/output.go new file mode 100644 index 0000000..845297e --- /dev/null +++ b/internal/module/output.go @@ -0,0 +1,28 @@ +package module + +import ( + "github.com/segmentio/terraform-docs/pkg/tfconf" +) + +// TerraformOutput is used for unmarshalling `terraform outputs --json` into +type TerraformOutput struct { + Sensitive bool `json:"sensitive"` + Type interface{} `json:"type"` + Value interface{} `json:"value"` +} + +type outputsSortedByName []*tfconf.Output + +func (a outputsSortedByName) Len() int { return len(a) } +func (a outputsSortedByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a outputsSortedByName) Less(i, j int) bool { + return a[i].Name < a[j].Name +} + +type outputsSortedByPosition []*tfconf.Output + +func (a outputsSortedByPosition) Len() int { return len(a) } +func (a outputsSortedByPosition) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a outputsSortedByPosition) Less(i, j int) bool { + return a[i].Position.Filename < a[j].Position.Filename || a[i].Position.Line < a[j].Position.Line +} diff --git a/internal/module/provider.go b/internal/module/provider.go new file mode 100644 index 0000000..440806d --- /dev/null +++ b/internal/module/provider.go @@ -0,0 +1,21 @@ +package module + +import ( + "github.com/segmentio/terraform-docs/pkg/tfconf" +) + +type providersSortedByName []*tfconf.Provider + +func (a providersSortedByName) Len() int { return len(a) } +func (a providersSortedByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a providersSortedByName) Less(i, j int) bool { + return a[i].Name < a[j].Name || (a[i].Name == a[j].Name && a[i].Alias < a[j].Alias) +} + +type providersSortedByPosition []*tfconf.Provider + +func (a providersSortedByPosition) Len() int { return len(a) } +func (a providersSortedByPosition) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a providersSortedByPosition) Less(i, j int) bool { + return a[i].Position.Filename < a[j].Position.Filename || a[i].Position.Line < a[j].Position.Line +} diff --git a/internal/pkg/print/print.go b/internal/pkg/print/print.go deleted file mode 100644 index 91d3830..0000000 --- a/internal/pkg/print/print.go +++ /dev/null @@ -1 +0,0 @@ -package print diff --git a/internal/pkg/tfconf/comment.go b/internal/pkg/tfconf/comment.go deleted file mode 100644 index f607ce7..0000000 --- a/internal/pkg/tfconf/comment.go +++ /dev/null @@ -1,29 +0,0 @@ -package tfconf - -import ( - "strings" - - "github.com/segmentio/terraform-docs/internal/pkg/reader" -) - -func readComment(filename string, lineNum int) string { - lines := reader.Lines{ - FileName: filename, - LineNum: lineNum, - Condition: func(line string) bool { - return strings.HasPrefix(line, "#") || strings.HasPrefix(line, "//") - }, - Parser: func(line string) (string, bool) { - line = strings.TrimSpace(line) - line = strings.TrimPrefix(line, "#") - line = strings.TrimPrefix(line, "//") - line = strings.TrimSpace(line) - return line, true - }, - } - comment, err := lines.Extract() - if err != nil { - return "" // absorb the error, we don't need to bubble it up or break the execution - } - return strings.Join(comment, " ") -} diff --git a/internal/pkg/tfconf/header.go b/internal/pkg/tfconf/header.go deleted file mode 100644 index 3b1b0c3..0000000 --- a/internal/pkg/tfconf/header.go +++ /dev/null @@ -1,41 +0,0 @@ -package tfconf - -import ( - "io/ioutil" - "path/filepath" - "strings" - - "github.com/segmentio/terraform-docs/internal/pkg/reader" -) - -func readHeader(path string) string { - filename := filepath.Join(path, "main.tf") - _, err := ioutil.ReadFile(filename) - if err != nil { - return "" // absorb the error, we don't need to bubble it up or break the execution - } - lines := reader.Lines{ - FileName: filename, - LineNum: -1, - Condition: func(line string) bool { - line = strings.TrimSpace(line) - return strings.HasPrefix(line, "/*") || strings.HasPrefix(line, "*") || strings.HasPrefix(line, "*/") - }, - Parser: func(line string) (string, bool) { - line = strings.TrimSpace(line) - if strings.HasPrefix(line, "/*") || strings.HasPrefix(line, "*/") { - return "", false - } - if line == "*" { - return "", true - } - line = strings.TrimPrefix(line, "* ") - return line, true - }, - } - header, err := lines.Extract() - if err != nil { - return "" // absorb the error, we don't need to bubble it up or break the execution - } - return strings.Join(header, "\n") -} diff --git a/internal/pkg/tfconf/input.go b/internal/pkg/tfconf/input.go deleted file mode 100644 index b901c6a..0000000 --- a/internal/pkg/tfconf/input.go +++ /dev/null @@ -1,85 +0,0 @@ -package tfconf - -import ( - "encoding/json" -) - -// Input represents a Terraform input. -type Input struct { - Name string `json:"name" yaml:"name"` - Type String `json:"type" yaml:"type"` - Description String `json:"description" yaml:"description"` - Default interface{} `json:"default" yaml:"default"` - Position Position `json:"-" yaml:"-"` -} - -// Value returns JSON representation of the 'Default' value, which is an 'interface'. -// If 'Default' is a primitive type, the primitive value of 'Default' will be returned -// and not the JSON formatted of it. -func (i *Input) Value() string { - marshaled, err := json.MarshalIndent(i.Default, "", " ") - if err != nil { - panic(err) - } - if value := string(marshaled); value != "null" { - return value - } - return "" -} - -// HasDefault indicates if a Terraform variable has a default value set. -func (i *Input) HasDefault() bool { - return i.Default != nil -} - -type inputsSortedByName []*Input - -func (a inputsSortedByName) Len() int { - return len(a) -} - -func (a inputsSortedByName) Swap(i, j int) { - a[i], a[j] = a[j], a[i] -} - -func (a inputsSortedByName) Less(i, j int) bool { - return a[i].Name < a[j].Name -} - -type inputsSortedByRequired []*Input - -func (a inputsSortedByRequired) Len() int { - return len(a) -} - -func (a inputsSortedByRequired) Swap(i, j int) { - a[i], a[j] = a[j], a[i] -} - -func (a inputsSortedByRequired) Less(i, j int) bool { - switch { - // i required, j not: i gets priority - case !a[i].HasDefault() && a[j].HasDefault(): - return true - // j required, i not: i does not get priority - case a[i].HasDefault() && !a[j].HasDefault(): - return false - // Otherwise, sort by name - default: - return a[i].Name < a[j].Name - } -} - -type inputsSortedByPosition []*Input - -func (a inputsSortedByPosition) Len() int { - return len(a) -} - -func (a inputsSortedByPosition) Swap(i, j int) { - a[i], a[j] = a[j], a[i] -} - -func (a inputsSortedByPosition) Less(i, j int) bool { - return a[i].Position.Filename < a[j].Position.Filename || a[i].Position.Line < a[j].Position.Line -} diff --git a/internal/pkg/tfconf/module.go b/internal/pkg/tfconf/module.go deleted file mode 100644 index 7d9665a..0000000 --- a/internal/pkg/tfconf/module.go +++ /dev/null @@ -1,226 +0,0 @@ -package tfconf - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "os/exec" - "sort" - "strings" - - "github.com/hashicorp/terraform-config-inspect/tfconfig" - "github.com/segmentio/terraform-docs/internal/pkg/print" -) - -// Module represents a Terraform mod. It consists of -// - Header ('header' json key): Module header found in shape of multi line comments at the beginning of 'main.tf' -// - Inputs ('inputs' json key): List of input 'variables' extracted from the Terraform module .tf files -// - Outputs ('outputs' json key): List of 'outputs' extracted from Terraform module .tf files -// - Providers ('providers' json key): List of 'providers' extracted from resources used in Terraform module -type Module struct { - Header string `json:"header" yaml:"header"` - Inputs []*Input `json:"inputs" yaml:"inputs"` - Outputs []*Output `json:"outputs" yaml:"outputs"` - Providers []*Provider `json:"providers" yaml:"providers"` - RequiredInputs []*Input `json:"-" yaml:"-"` - OptionalInputs []*Input `json:"-" yaml:"-"` -} - -// HasInputs indicates if the document has inputs. -func (m *Module) HasInputs() bool { - return len(m.Inputs) > 0 -} - -// HasOutputs indicates if the document has outputs. -func (m *Module) HasOutputs() bool { - return len(m.Outputs) > 0 -} - -// Sort sorts list of inputs and outputs based on provided flags (name, required, etc) -func (m *Module) Sort(settings *print.Settings) { - if settings.SortByName { - sort.Sort(providersSortedByName(m.Providers)) - } else { - sort.Sort(providersSortedByPosition(m.Providers)) - } - - if settings.SortByName { - if settings.SortByRequired { - sort.Sort(inputsSortedByRequired(m.Inputs)) - sort.Sort(inputsSortedByRequired(m.RequiredInputs)) - sort.Sort(inputsSortedByRequired(m.OptionalInputs)) - } else { - sort.Sort(inputsSortedByName(m.Inputs)) - sort.Sort(inputsSortedByName(m.RequiredInputs)) - sort.Sort(inputsSortedByName(m.OptionalInputs)) - } - } else { - sort.Sort(inputsSortedByPosition(m.Inputs)) - sort.Sort(inputsSortedByPosition(m.RequiredInputs)) - sort.Sort(inputsSortedByPosition(m.OptionalInputs)) - } - - if settings.SortByName { - sort.Sort(outputsSortedByName(m.Outputs)) - } else { - sort.Sort(outputsSortedByPosition(m.Outputs)) - } -} - -// CreateModule returns new instance of Module with all the inputs and -// outputs discovered from provided 'path' containing Terraform config -func CreateModule(options *Options) (*Module, error) { - mod := loadModule(options.Path) - - header := readHeader(options.Path) - - var inputs = make([]*Input, 0, len(mod.Variables)) - var requiredInputs = make([]*Input, 0, len(mod.Variables)) - var optionalInputs = make([]*Input, 0, len(mod.Variables)) - - for _, input := range mod.Variables { - inputType := input.Type - if input.Type == "" { - inputType = "any" - if input.Default != nil { - switch xType := fmt.Sprintf("%T", input.Default); xType { - case "string": - inputType = "string" - case "int", "int8", "int16", "int32", "int64", "float32", "float64": - inputType = "number" - case "bool": - inputType = "bool" - case "[]interface {}": - inputType = "list" - case "map[string]interface {}": - inputType = "map" - } - } - } - - inputDescription := input.Description - if inputDescription == "" { - inputDescription = readComment(input.Pos.Filename, input.Pos.Line-1) - } - - i := &Input{ - Name: input.Name, - Type: String(inputType), - Description: String(inputDescription), - Default: input.Default, - Position: Position{ - Filename: input.Pos.Filename, - Line: input.Pos.Line, - }, - } - - inputs = append(inputs, i) - if i.HasDefault() { - optionalInputs = append(optionalInputs, i) - } else { - requiredInputs = append(requiredInputs, i) - } - } - - // output module - var outputs = make([]*Output, 0, len(mod.Outputs)) - for _, o := range mod.Outputs { - outputDescription := o.Description - if outputDescription == "" { - outputDescription = readComment(o.Pos.Filename, o.Pos.Line-1) - } - output := &Output{ - Name: o.Name, - Description: String(outputDescription), - Position: Position{ - Filename: o.Pos.Filename, - Line: o.Pos.Line, - }, - } - if options.OutputValues { - terraformOutputs, err := loadOutputValues(options) - if err != nil { - log.Fatal(err) - } - - if terraformOutputs[output.Name].Sensitive { - output.Value = "" - } else { - output.Value = terraformOutputs[output.Name].Value - } - } - outputs = append(outputs, output) - } - - var providerSet = loadProviders(mod.RequiredProviders, mod.ManagedResources, mod.DataResources) - var providers = make([]*Provider, 0, len(providerSet)) - for _, provider := range providerSet { - providers = append(providers, provider) - } - - module := &Module{ - Header: header, - Inputs: inputs, - Outputs: outputs, - Providers: providers, - RequiredInputs: requiredInputs, - OptionalInputs: optionalInputs, - } - return module, nil -} - -func loadModule(path string) *tfconfig.Module { - module, diag := tfconfig.LoadModule(path) - if diag != nil && diag.HasErrors() { - log.Fatal(diag) - } - return module -} - -func loadProviders(requiredProviders map[string]*tfconfig.ProviderRequirement, resources ...map[string]*tfconfig.Resource) map[string]*Provider { - var providers = make(map[string]*Provider) - for _, resource := range resources { - for _, r := range resource { - var version = "" - if requiredVersion, ok := requiredProviders[r.Provider.Name]; ok && len(requiredVersion.VersionConstraints) > 0 { - version = strings.Join(requiredVersion.VersionConstraints, " ") - } - key := fmt.Sprintf("%s.%s", r.Provider.Name, r.Provider.Alias) - providers[key] = &Provider{ - Name: r.Provider.Name, - Alias: String(r.Provider.Alias), - Version: String(version), - Position: Position{ - Filename: r.Pos.Filename, - Line: r.Pos.Line, - }, - } - } - } - return providers -} - -func loadOutputValues(options *Options) (map[string]*TerraformOutput, error) { - var out []byte - var err error - - if options.OutputValuesPath == "" { - cmd := exec.Command("terraform", "output", "-json") - cmd.Dir = options.Path - if out, err = cmd.Output(); err != nil { - return nil, fmt.Errorf("caught error while reading the terraform outputs: %v", err) - } - } else { - if out, err = ioutil.ReadFile(options.OutputValuesPath); err != nil { - return nil, fmt.Errorf("caught error while reading the terraform outputs file at %s: %v", options.OutputValuesPath, err) - } - } - - var terraformOutputs map[string]*TerraformOutput - err = json.Unmarshal(out, &terraformOutputs) - if err != nil { - return nil, err - } - return terraformOutputs, err -} diff --git a/internal/pkg/tfconf/options.go b/internal/pkg/tfconf/options.go deleted file mode 100644 index 61ce408..0000000 --- a/internal/pkg/tfconf/options.go +++ /dev/null @@ -1,8 +0,0 @@ -package tfconf - -// Options contains required options to load a Module from path -type Options struct { - Path string - OutputValues bool - OutputValuesPath string -} diff --git a/internal/pkg/tfconf/output.go b/internal/pkg/tfconf/output.go deleted file mode 100644 index 0f8abca..0000000 --- a/internal/pkg/tfconf/output.go +++ /dev/null @@ -1,44 +0,0 @@ -package tfconf - -// Output represents a Terraform output. -type Output struct { - Name string `json:"name" yaml:"name"` - Description String `json:"description" yaml:"description"` - Value interface{} `json:"value,omitempty" yaml:"value,omitempty"` - Position Position `json:"-" yaml:"-"` -} - -type outputsSortedByName []*Output - -func (a outputsSortedByName) Len() int { - return len(a) -} - -func (a outputsSortedByName) Swap(i, j int) { - a[i], a[j] = a[j], a[i] -} - -func (a outputsSortedByName) Less(i, j int) bool { - return a[i].Name < a[j].Name -} - -type outputsSortedByPosition []*Output - -func (a outputsSortedByPosition) Len() int { - return len(a) -} - -func (a outputsSortedByPosition) Swap(i, j int) { - a[i], a[j] = a[j], a[i] -} - -func (a outputsSortedByPosition) Less(i, j int) bool { - return a[i].Position.Filename < a[j].Position.Filename || a[i].Position.Line < a[j].Position.Line -} - -// TerraformOutput is used for unmarshalling `terraform outputs --json` into -type TerraformOutput struct { - Sensitive bool `json:"sensitive"` - Type interface{} `json:"type"` - Value interface{} `json:"value"` -} diff --git a/internal/pkg/tfconf/provider.go b/internal/pkg/tfconf/provider.go deleted file mode 100644 index 3b8b43b..0000000 --- a/internal/pkg/tfconf/provider.go +++ /dev/null @@ -1,49 +0,0 @@ -package tfconf - -import ( - "fmt" -) - -// Provider represents a Terraform output. -type Provider struct { - Name string `json:"name" yaml:"name"` - Alias String `json:"alias" yaml:"alias"` - Version String `json:"version" yaml:"version"` - Position Position `json:"-" yaml:"-"` -} - -// FullName returns full name of the provider, with alias if available -func (p *Provider) FullName() string { - if p.Alias != "" { - return fmt.Sprintf("%s.%s", p.Name, p.Alias) - } - return p.Name -} - -type providersSortedByName []*Provider - -func (a providersSortedByName) Len() int { - return len(a) -} - -func (a providersSortedByName) Swap(i, j int) { - a[i], a[j] = a[j], a[i] -} - -func (a providersSortedByName) Less(i, j int) bool { - return a[i].Name < a[j].Name || (a[i].Name == a[j].Name && a[i].Alias < a[j].Alias) -} - -type providersSortedByPosition []*Provider - -func (a providersSortedByPosition) Len() int { - return len(a) -} - -func (a providersSortedByPosition) Swap(i, j int) { - a[i], a[j] = a[j], a[i] -} - -func (a providersSortedByPosition) Less(i, j int) bool { - return a[i].Position.Filename < a[j].Position.Filename || a[i].Position.Line < a[j].Position.Line -} diff --git a/internal/pkg/reader/lines.go b/internal/reader/lines.go similarity index 100% rename from internal/pkg/reader/lines.go rename to internal/reader/lines.go diff --git a/internal/pkg/testutil/settings.go b/internal/testutil/settings.go similarity index 96% rename from internal/pkg/testutil/settings.go rename to internal/testutil/settings.go index 1838cf1..b710d01 100644 --- a/internal/pkg/testutil/settings.go +++ b/internal/testutil/settings.go @@ -2,7 +2,7 @@ package testutil import ( "github.com/imdario/mergo" - "github.com/segmentio/terraform-docs/internal/pkg/print" + "github.com/segmentio/terraform-docs/pkg/print" ) // TestSettings respresents the Settings instance for tests diff --git a/internal/pkg/testutil/testing.go b/internal/testutil/testing.go similarity index 61% rename from internal/pkg/testutil/testing.go rename to internal/testutil/testing.go index 56b7b42..e90dc5d 100644 --- a/internal/pkg/testutil/testing.go +++ b/internal/testutil/testing.go @@ -6,31 +6,30 @@ import ( "path/filepath" "runtime" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/internal/module" + "github.com/segmentio/terraform-docs/pkg/tfconf" ) -// GetModule returns what is generated by the test params -func GetModule(options *tfconf.Options) (*tfconf.Module, error) { +// GetModule returns 'example' Module +func GetModule(options *module.Options) (*tfconf.Module, error) { path, err := getExampleFolder() if err != nil { return nil, err } options.Path = path - if options.OutputValues { options.OutputValuesPath = filepath.Join(path, options.OutputValuesPath) } - - module, err := tfconf.CreateModule(options) + tfmodule, err := module.LoadWithOptions(options) if err != nil { return nil, err } - return module, err + return tfmodule, nil } // GetExpected returns 'example' Module and expected Golden file content -func GetExpected(name string) (string, error) { - path := filepath.Join(testDataPath(), name+".golden") +func GetExpected(format, name string) (string, error) { + path := filepath.Join(testDataPath(), format, name+".golden") bytes, err := ioutil.ReadFile(path) if err != nil { return "", err @@ -40,7 +39,7 @@ func GetExpected(name string) (string, error) { func getExampleFolder() (string, error) { _, b, _, _ := runtime.Caller(0) - path := filepath.Join(filepath.Dir(b), "..", "..", "..", "examples") + path := filepath.Join(filepath.Dir(b), "..", "..", "examples") if _, err := os.Stat(path); os.IsNotExist(err) { return "", err } diff --git a/internal/pkg/tfconf/string.go b/internal/types/string.go similarity index 98% rename from internal/pkg/tfconf/string.go rename to internal/types/string.go index 052c32b..2ce3045 100644 --- a/internal/pkg/tfconf/string.go +++ b/internal/types/string.go @@ -1,4 +1,4 @@ -package tfconf +package types import ( "bytes" diff --git a/internal/pkg/version/version.go b/internal/version/version.go similarity index 99% rename from internal/pkg/version/version.go rename to internal/version/version.go index edde678..6148fb5 100644 --- a/internal/pkg/version/version.go +++ b/internal/version/version.go @@ -22,11 +22,9 @@ func init() { if version == "" { version = dev } - if commitHash == "" { commitHash = dev } - if buildDate == "" { buildDate = time.Now().Format(time.RFC3339) } @@ -37,8 +35,6 @@ func Version() string { if !strings.HasSuffix(version, commitHash) { version += " " + commitHash } - osArch := runtime.GOOS + "/" + runtime.GOARCH - return fmt.Sprintf("%s %s BuildDate: %s", version, osArch, buildDate) } diff --git a/pkg/print/doc.go b/pkg/print/doc.go new file mode 100644 index 0000000..47e53ba --- /dev/null +++ b/pkg/print/doc.go @@ -0,0 +1,2 @@ +// Package print provides a specific definition of a printer Format +package print diff --git a/pkg/print/print.go b/pkg/print/print.go new file mode 100644 index 0000000..2fc8c4f --- /dev/null +++ b/pkg/print/print.go @@ -0,0 +1,10 @@ +package print + +import ( + "github.com/segmentio/terraform-docs/pkg/tfconf" +) + +// Format represents a printer format (e.g. json, table, yaml, ...) +type Format interface { + Print(*tfconf.Module, *Settings) (string, error) +} diff --git a/internal/pkg/print/settings.go b/pkg/print/settings.go similarity index 100% rename from internal/pkg/print/settings.go rename to pkg/print/settings.go diff --git a/pkg/tfconf/doc.go b/pkg/tfconf/doc.go new file mode 100644 index 0000000..089e108 --- /dev/null +++ b/pkg/tfconf/doc.go @@ -0,0 +1,2 @@ +// Package tfconf is the representation of a Terraform Module +package tfconf diff --git a/pkg/tfconf/input.go b/pkg/tfconf/input.go new file mode 100644 index 0000000..c190c32 --- /dev/null +++ b/pkg/tfconf/input.go @@ -0,0 +1,35 @@ +package tfconf + +import ( + "encoding/json" + + "github.com/segmentio/terraform-docs/internal/types" +) + +// Input represents a Terraform input. +type Input struct { + Name string `json:"name" yaml:"name"` + Type types.String `json:"type" yaml:"type"` + Description types.String `json:"description" yaml:"description"` + Default interface{} `json:"default" yaml:"default"` + Position Position `json:"-" yaml:"-"` +} + +// Value returns JSON representation of the 'Default' value, which is an 'interface'. +// If 'Default' is a primitive type, the primitive value of 'Default' will be returned +// and not the JSON formatted of it. +func (i *Input) Value() string { + marshaled, err := json.MarshalIndent(i.Default, "", " ") + if err != nil { + panic(err) + } + if value := string(marshaled); value != "null" { + return value + } + return "" +} + +// HasDefault indicates if a Terraform variable has a default value set. +func (i *Input) HasDefault() bool { + return i.Default != nil +} diff --git a/pkg/tfconf/module.go b/pkg/tfconf/module.go new file mode 100644 index 0000000..afc3e53 --- /dev/null +++ b/pkg/tfconf/module.go @@ -0,0 +1,36 @@ +package tfconf + +// Module represents a Terraform module. It consists of +// - Header ('header' json key): Module header found in shape of multi line comments at the beginning of 'main.tf' +// - Inputs ('inputs' json key): List of input 'variables' extracted from the Terraform module .tf files +// - Outputs ('outputs' json key): List of 'outputs' extracted from Terraform module .tf files +// - Providers ('providers' json key): List of 'providers' extracted from resources used in Terraform module +type Module struct { + Header string `json:"header" yaml:"header"` + Inputs []*Input `json:"inputs" yaml:"inputs"` + Outputs []*Output `json:"outputs" yaml:"outputs"` + Providers []*Provider `json:"providers" yaml:"providers"` + + RequiredInputs []*Input `json:"-" yaml:"-"` + OptionalInputs []*Input `json:"-" yaml:"-"` +} + +// HasHeader indicates if the module has header. +func (m *Module) HasHeader() bool { + return len(m.Header) > 0 +} + +// HasInputs indicates if the module has inputs. +func (m *Module) HasInputs() bool { + return len(m.Inputs) > 0 +} + +// HasOutputs indicates if the module has outputs. +func (m *Module) HasOutputs() bool { + return len(m.Outputs) > 0 +} + +// HasProviders indicates if the module has providers. +func (m *Module) HasProviders() bool { + return len(m.Providers) > 0 +} diff --git a/pkg/tfconf/output.go b/pkg/tfconf/output.go new file mode 100644 index 0000000..1bb53de --- /dev/null +++ b/pkg/tfconf/output.go @@ -0,0 +1,13 @@ +package tfconf + +import ( + "github.com/segmentio/terraform-docs/internal/types" +) + +// Output represents a Terraform output. +type Output struct { + Name string `json:"name" yaml:"name"` + Description types.String `json:"description" yaml:"description"` + Value interface{} `json:"value,omitempty" yaml:"value,omitempty"` + Position Position `json:"-" yaml:"-"` +} diff --git a/internal/pkg/tfconf/position.go b/pkg/tfconf/position.go similarity index 100% rename from internal/pkg/tfconf/position.go rename to pkg/tfconf/position.go diff --git a/pkg/tfconf/provider.go b/pkg/tfconf/provider.go new file mode 100644 index 0000000..e14e521 --- /dev/null +++ b/pkg/tfconf/provider.go @@ -0,0 +1,23 @@ +package tfconf + +import ( + "fmt" + + "github.com/segmentio/terraform-docs/internal/types" +) + +// Provider represents a Terraform output. +type Provider struct { + Name string `json:"name" yaml:"name"` + Alias types.String `json:"alias" yaml:"alias"` + Version types.String `json:"version" yaml:"version"` + Position Position `json:"-" yaml:"-"` +} + +// FullName returns full name of the provider, with alias if available +func (p *Provider) FullName() string { + if p.Alias != "" { + return fmt.Sprintf("%s.%s", p.Name, p.Alias) + } + return p.Name +} diff --git a/pkg/tmpl/doc.go b/pkg/tmpl/doc.go new file mode 100644 index 0000000..cbe4e15 --- /dev/null +++ b/pkg/tmpl/doc.go @@ -0,0 +1,2 @@ +// Package tmpl provides templating functionality +package tmpl diff --git a/internal/pkg/print/markdown/markdown.go b/pkg/tmpl/sanitizer.go similarity index 60% rename from internal/pkg/print/markdown/markdown.go rename to pkg/tmpl/sanitizer.go index e02a2f7..6652543 100644 --- a/internal/pkg/print/markdown/markdown.go +++ b/pkg/tmpl/sanitizer.go @@ -1,4 +1,4 @@ -package markdown +package tmpl import ( "bytes" @@ -6,37 +6,12 @@ import ( "regexp" "strings" - "github.com/segmentio/terraform-docs/internal/pkg/print" - + "github.com/segmentio/terraform-docs/pkg/print" "mvdan.cc/xurls/v2" ) -// Sanitize cleans a Markdown document to soothe linters. -func Sanitize(markdown string) string { - result := markdown - - // Preserve double spaces at the end of the line - result = regexp.MustCompile(` {2}(\r?\n)`).ReplaceAllString(result, "‡‡$1") - - // Remove trailing spaces from the end of lines - result = regexp.MustCompile(` +(\r?\n)`).ReplaceAllString(result, "$1") - result = regexp.MustCompile(` +$`).ReplaceAllLiteralString(result, "") - - // Preserve double spaces at the end of the line - result = regexp.MustCompile(`‡‡(\r?\n)`).ReplaceAllString(result, " $1") - - // Remove blank line with only double spaces in it - result = regexp.MustCompile(`(\r?\n) (\r?\n)`).ReplaceAllString(result, "$1") - - // Remove multiple consecutive blank lines - result = regexp.MustCompile(`(\r?\n){3,}`).ReplaceAllString(result, "$1$1") - result = regexp.MustCompile(`(\r?\n){2,}$`).ReplaceAllString(result, "$1") - - return result -} - -// SanitizeName escapes underscore character which have special meaning in Markdown. -func SanitizeName(name string, settings *print.Settings) string { +// sanitizeName escapes underscore character which have special meaning in Markdown. +func sanitizeName(name string, settings *print.Settings) string { if settings.EscapeCharacters { // Escape underscore name = strings.Replace(name, "_", "\\_", -1) @@ -44,9 +19,9 @@ func SanitizeName(name string, settings *print.Settings) string { return name } -// SanitizeItemForDocument converts passed 'string' to suitable Markdown representation +// sanitizeItemForDocument converts passed 'string' to suitable Markdown representation // for a document. (including line-break, illegal characters, code blocks etc) -func SanitizeItemForDocument(s string, settings *print.Settings) string { +func sanitizeItemForDocument(s string, settings *print.Settings) string { if s == "" { return "n/a" } @@ -54,9 +29,9 @@ func SanitizeItemForDocument(s string, settings *print.Settings) string { s, "```", func(segment string) string { - segment = ConvertMultiLineText(segment, false) - segment = EscapeIllegalCharacters(segment, settings) - segment = NormalizeURLs(segment, settings) + segment = convertMultiLineText(segment, false) + segment = escapeIllegalCharacters(segment, settings) + segment = normalizeURLs(segment, settings) return segment }, func(segment string) string { @@ -71,9 +46,9 @@ func SanitizeItemForDocument(s string, settings *print.Settings) string { return strings.Replace(result, "
", "\n", -1) } -// SanitizeItemForTable converts passed 'string' to suitable Markdown representation +// sanitizeItemForTable converts passed 'string' to suitable Markdown representation // for a table. (including line-break, illegal characters, code blocks etc) -func SanitizeItemForTable(s string, settings *print.Settings) string { +func sanitizeItemForTable(s string, settings *print.Settings) string { if s == "" { return "n/a" } @@ -81,9 +56,9 @@ func SanitizeItemForTable(s string, settings *print.Settings) string { s, "```", func(segment string) string { - segment = ConvertMultiLineText(segment, true) - segment = EscapeIllegalCharacters(segment, settings) - segment = NormalizeURLs(segment, settings) + segment = convertMultiLineText(segment, true) + segment = escapeIllegalCharacters(segment, settings) + segment = normalizeURLs(segment, settings) return segment }, func(segment string) string { @@ -97,8 +72,8 @@ func SanitizeItemForTable(s string, settings *print.Settings) string { return result } -// ConvertMultiLineText converts a multi-line text into a suitable Markdown representation. -func ConvertMultiLineText(s string, isTable bool) string { +// convertMultiLineText converts a multi-line text into a suitable Markdown representation. +func convertMultiLineText(s string, isTable bool) string { if isTable { s = strings.TrimSpace(s) } @@ -125,8 +100,8 @@ func ConvertMultiLineText(s string, isTable bool) string { return s } -// EscapeIllegalCharacters escapes characters which have special meaning in Markdown into their corresponding literal. -func EscapeIllegalCharacters(s string, settings *print.Settings) string { +// escapeIllegalCharacters escapes characters which have special meaning in Markdown into their corresponding literal. +func escapeIllegalCharacters(s string, settings *print.Settings) string { // Escape pipe if settings.EscapePipe { s = strings.Replace(s, "|", "\\|", -1) @@ -160,10 +135,10 @@ func EscapeIllegalCharacters(s string, settings *print.Settings) string { return s } -// NormalizeURLs runs after escape function and normalizes URL back +// normalizeURLs runs after escape function and normalizes URL back // to the original state. For example any underscore in the URL which // got escaped by 'EscapeIllegalCharacters' will be reverted back. -func NormalizeURLs(s string, settings *print.Settings) string { +func normalizeURLs(s string, settings *print.Settings) string { if settings.EscapeCharacters { if urls := xurls.Strict().FindAllString(s, -1); len(urls) > 0 { for _, url := range urls { @@ -175,11 +150,11 @@ func NormalizeURLs(s string, settings *print.Settings) string { return s } -// GenerateIndentation generates indentation of Markdown headers +// generateIndentation generates indentation of Markdown headers // with base level of provided 'settings.MarkdownIndent' plus any // extra level needed for subsection (e.g. 'Required Inputs' which // is a subsection of 'Inputs' section) -func GenerateIndentation(extra int, settings *print.Settings) string { +func generateIndentation(extra int, settings *print.Settings) string { var base = settings.MarkdownIndent if base < 1 || base > 5 { base = 2 @@ -191,18 +166,6 @@ func GenerateIndentation(extra int, settings *print.Settings) string { return indent } -// PrintFencedCodeBlock prints codes in fences, it automatically detects if -// the input 'code' contains '\n' it will use multi line fence, otherwise it -// wraps the 'code' inside single-tick block. -// If the fenced is multi-line it also appens an extra '\n` at the end and -// returns true accordingly, otherwise returns false for non-carriage return. -func PrintFencedCodeBlock(code string, language string) (string, bool) { - if strings.Contains(code, "\n") { - return fmt.Sprintf("\n\n```%s\n%s\n```\n", language, code), true - } - return fmt.Sprintf("`%s`", code), false -} - func processSegments(s string, prefix string, normalFn func(segment string) string, codeFn func(segment string) string) string { // Isolate blocks of code. Dont escape anything inside them nextIsInCodeBlock := strings.HasPrefix(s, prefix) diff --git a/internal/pkg/tmpl/template.go b/pkg/tmpl/template.go similarity index 85% rename from internal/pkg/tmpl/template.go rename to pkg/tmpl/template.go index 0b21e70..751a12e 100644 --- a/internal/pkg/tmpl/template.go +++ b/pkg/tmpl/template.go @@ -6,9 +6,9 @@ import ( "strings" "text/template" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/print/markdown" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/internal/types" + "github.com/segmentio/terraform-docs/pkg/print" + "github.com/segmentio/terraform-docs/pkg/tfconf" ) // Item represents a named templated which can reference @@ -22,9 +22,10 @@ type Item struct { // to be rendered with provided settings with use of built-in and // custom functions type Template struct { - Items []*Item + Items []*Item + settings *print.Settings - funcMap map[string]interface{} + funcMap template.FuncMap } // NewTemplate returns new instance of Template @@ -39,7 +40,7 @@ func NewTemplate(items ...*Item) *Template { // CustomFunc adds new custom functions to the template // if functions with the same names didn't exist -func (t *Template) CustomFunc(funcs map[string]interface{}) { +func (t *Template) CustomFunc(funcs template.FuncMap) { for name, fn := range funcs { if t.funcMap[name] == nil { t.funcMap[name] = fn @@ -85,7 +86,7 @@ func (t *Template) Render(module *tfconf.Module) (string, error) { return buffer.String(), nil } -func builtinFuncs(settings *print.Settings) map[string]interface{} { +func builtinFuncs(settings *print.Settings) template.FuncMap { return template.FuncMap{ "default": func(d string, s string) string { if s != "" { @@ -108,7 +109,7 @@ func builtinFuncs(settings *print.Settings) map[string]interface{} { } return f }, - "tostring": func(s tfconf.String) string { + "tostring": func(s types.String) string { return string(s) }, "trim": func(t string, s string) string { @@ -143,22 +144,22 @@ func builtinFuncs(settings *print.Settings) map[string]interface{} { }, "indent": func(l int) string { - return markdown.GenerateIndentation(l, settings) + return generateIndentation(l, settings) }, "name": func(n string) string { - return markdown.SanitizeName(n, settings) + return sanitizeName(n, settings) }, "sanitizeHeader": func(s string) string { settings.EscapePipe = false - s = markdown.SanitizeItemForDocument(s, settings) + s = sanitizeItemForDocument(s, settings) settings.EscapePipe = true return s }, "sanitizeDoc": func(s string) string { - return markdown.SanitizeItemForDocument(s, settings) + return sanitizeItemForDocument(s, settings) }, "sanitizeTbl": func(s string) string { - return markdown.SanitizeItemForTable(s, settings) + return sanitizeItemForTable(s, settings) }, "sanitizeInterface": func(i interface{}) string { var v string diff --git a/scripts/docs/generate.go b/scripts/docs/generate.go index cacdfc9..32667c1 100644 --- a/scripts/docs/generate.go +++ b/scripts/docs/generate.go @@ -11,13 +11,9 @@ import ( "time" "github.com/segmentio/terraform-docs/cmd" - "github.com/segmentio/terraform-docs/internal/pkg/print" - "github.com/segmentio/terraform-docs/internal/pkg/print/json" - "github.com/segmentio/terraform-docs/internal/pkg/print/markdown/document" - "github.com/segmentio/terraform-docs/internal/pkg/print/markdown/table" - "github.com/segmentio/terraform-docs/internal/pkg/print/pretty" - "github.com/segmentio/terraform-docs/internal/pkg/print/yaml" - "github.com/segmentio/terraform-docs/internal/pkg/tfconf" + "github.com/segmentio/terraform-docs/internal/format" + "github.com/segmentio/terraform-docs/internal/module" + "github.com/segmentio/terraform-docs/pkg/print" "github.com/spf13/cobra" ) @@ -125,20 +121,18 @@ func printOptions(buf *bytes.Buffer, cmd *cobra.Command, name string) error { return nil } -type printer func(*tfconf.Module, *print.Settings) (string, error) - -func getPrinter(name string) printer { +func getPrinter(name string, settings *print.Settings) print.Format { switch strings.Replace(name, "terraform-docs ", "", -1) { case "json": - return json.Print + return format.NewJSON(settings) case "markdown document": - return document.Print + return format.NewDocument(settings) case "markdown table": - return table.Print + return format.NewTable(settings) case "pretty": - return pretty.Print + return format.NewPretty(settings) case "yaml": - return yaml.Print + return format.NewYAML(settings) } return nil } @@ -159,20 +153,22 @@ func printExample(buf *bytes.Buffer, name string) error { buf.WriteString("```\n\n") buf.WriteString("generates the following output:\n\n") - options := &tfconf.Options{ + settings := print.NewSettings() + settings.ShowColor = false + options := &module.Options{ Path: "./examples", + SortBy: &module.SortBy{ + Name: settings.SortByName, + Required: settings.SortByRequired, + }, } - module, err := tfconf.CreateModule(options) + tfmodule, err := module.LoadWithOptions(options) if err != nil { log.Fatal(err) } - // fmt.Println(name) - if fn := getPrinter(name); fn != nil { - settings := print.NewSettings() - settings.ShowColor = false - - output, err := fn(module, settings) + if printer := getPrinter(name, settings); printer != nil { + output, err := printer.Print(tfmodule, settings) if err != nil { return err } diff --git a/scripts/release/release.sh b/scripts/release/release.sh index 116aacd..401d29b 100755 --- a/scripts/release/release.sh +++ b/scripts/release/release.sh @@ -58,11 +58,11 @@ CLOSEST_VERSION=$(getClosestVersion) # Bump the released version in README and version.go sed -i -E 's|'${CLOSEST_VERSION}'|v'${RELEASE_VERSION}'|g' README.md -sed -i -E 's|v'${RELEASE_VERSION}'-alpha|v'${RELEASE_VERSION}'|g' internal/pkg/version/version.go +sed -i -E 's|v'${RELEASE_VERSION}'-alpha|v'${RELEASE_VERSION}'|g' internal/version/version.go # Commit changes printf "\033[36m==> %s\033[0m\n" "Commit changes for release version v${RELEASE_VERSION}" -git add README.md internal/pkg/version/version.go +git add README.md internal/version/version.go git commit -m "Release version v${RELEASE_VERSION}" printf "\033[36m==> %s\033[0m\n" "Push commits for v${RELEASE_VERSION}" @@ -80,11 +80,11 @@ git push origin v${RELEASE_VERSION} # Bump the next version in version.go NEXT_VERSION=$(echo "${RELEASE_VERSION}" | sed 's/^v//' | awk -F'[ .]' '{print $1"."$2+1".0"}') -sed -i -E 's|v'${RELEASE_VERSION}'|v'${NEXT_VERSION}'-alpha|g' internal/pkg/version/version.go +sed -i -E 's|v'${RELEASE_VERSION}'|v'${NEXT_VERSION}'-alpha|g' internal/version/version.go # Commit changes printf "\033[36m==> %s\033[0m\n" "Bump version to v${NEXT_VERSION}-alpha" -git add internal/pkg/version/version.go +git add internal/version/version.go git commit -m "Bump version to v${NEXT_VERSION}-alpha" printf "\033[36m==> %s\033[0m\n" "Push commits for v${NEXT_VERSION}-alpha"