github.com/opentofu/opentofu@v1.7.1/internal/command/version.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package command 7 8 import ( 9 "bytes" 10 "encoding/json" 11 "fmt" 12 "sort" 13 "strings" 14 15 "github.com/opentofu/opentofu/internal/addrs" 16 "github.com/opentofu/opentofu/internal/depsfile" 17 "github.com/opentofu/opentofu/internal/getproviders" 18 ) 19 20 // VersionCommand is a Command implementation prints the version. 21 type VersionCommand struct { 22 Meta 23 24 Version string 25 VersionPrerelease string 26 Platform getproviders.Platform 27 } 28 29 type VersionOutput struct { 30 Version string `json:"terraform_version"` 31 Platform string `json:"platform"` 32 ProviderSelections map[string]string `json:"provider_selections"` 33 } 34 35 func (c *VersionCommand) Help() string { 36 helpText := ` 37 Usage: tofu [global options] version [options] 38 39 Displays the version of OpenTofu and all installed plugins 40 41 Options: 42 43 -json Output the version information as a JSON object. 44 ` 45 return strings.TrimSpace(helpText) 46 } 47 48 func (c *VersionCommand) Run(args []string) int { 49 var versionString bytes.Buffer 50 args = c.Meta.process(args) 51 var jsonOutput bool 52 cmdFlags := c.Meta.defaultFlagSet("version") 53 cmdFlags.BoolVar(&jsonOutput, "json", false, "json") 54 // Enable but ignore the global version flags. In main.go, if any of the 55 // arguments are -v, -version, or --version, this command will be called 56 // with the rest of the arguments, so we need to be able to cope with 57 // those. 58 cmdFlags.Bool("v", true, "version") 59 cmdFlags.Bool("version", true, "version") 60 cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } 61 if err := cmdFlags.Parse(args); err != nil { 62 c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error())) 63 return 1 64 } 65 66 fmt.Fprintf(&versionString, "OpenTofu v%s", c.Version) 67 if c.VersionPrerelease != "" { 68 fmt.Fprintf(&versionString, "-%s", c.VersionPrerelease) 69 } 70 71 // We'll also attempt to print out the selected plugin versions. We do 72 // this based on the dependency lock file, and so the result might be 73 // empty or incomplete if the user hasn't successfully run "tofu init" 74 // since the most recent change to dependencies. 75 // 76 // Generally-speaking this is a best-effort thing that will give us a good 77 // result in the usual case where the user successfully ran "tofu init" 78 // and then hit a problem running _another_ command. 79 var providerVersions []string 80 var providerLocks map[addrs.Provider]*depsfile.ProviderLock 81 if locks, err := c.lockedDependencies(); err == nil { 82 providerLocks = locks.AllProviders() 83 for providerAddr, lock := range providerLocks { 84 version := lock.Version().String() 85 if version == "0.0.0" { 86 providerVersions = append(providerVersions, fmt.Sprintf("+ provider %s (unversioned)", providerAddr)) 87 } else { 88 providerVersions = append(providerVersions, fmt.Sprintf("+ provider %s v%s", providerAddr, version)) 89 } 90 } 91 } 92 93 if jsonOutput { 94 selectionsOutput := make(map[string]string) 95 for providerAddr, lock := range providerLocks { 96 version := lock.Version().String() 97 selectionsOutput[providerAddr.String()] = version 98 } 99 100 var versionOutput string 101 if c.VersionPrerelease != "" { 102 versionOutput = c.Version + "-" + c.VersionPrerelease 103 } else { 104 versionOutput = c.Version 105 } 106 107 output := VersionOutput{ 108 Version: versionOutput, 109 Platform: c.Platform.String(), 110 ProviderSelections: selectionsOutput, 111 } 112 113 jsonOutput, err := json.MarshalIndent(output, "", " ") 114 if err != nil { 115 c.Ui.Error(fmt.Sprintf("\nError marshalling JSON: %s", err)) 116 return 1 117 } 118 c.Ui.Output(string(jsonOutput)) 119 return 0 120 } else { 121 c.Ui.Output(versionString.String()) 122 c.Ui.Output(fmt.Sprintf("on %s", c.Platform)) 123 124 if len(providerVersions) != 0 { 125 sort.Strings(providerVersions) 126 for _, str := range providerVersions { 127 c.Ui.Output(str) 128 } 129 } 130 } 131 132 return 0 133 } 134 135 func (c *VersionCommand) Synopsis() string { 136 return "Show the current OpenTofu version" 137 }