Skip to content

Commit

Permalink
Merge pull request #6 from tfversion/feature/required-version
Browse files Browse the repository at this point in the history
Feature: install/use required version
  • Loading branch information
ChrisTerBeke committed Feb 27, 2024
2 parents 068332d + 49b3105 commit 0b90ebf
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 49 deletions.
1 change: 0 additions & 1 deletion .golangci.yml
Expand Up @@ -20,7 +20,6 @@ linters:
- exportloopref
- forcetypeassert
- funlen
- godot
- godox
- gofmt
- gosimple
Expand Down
12 changes: 12 additions & 0 deletions README.md
Expand Up @@ -38,6 +38,12 @@ tfversion install --latest
tfversion install --latest --pre-release
```

### Install the required version for your current directory

```sh
tfversion install --required
```

### Use a specific version

```sh
Expand All @@ -56,6 +62,12 @@ tfversion use --latest
tfversion use --latest --pre-release
```

### Use the required version for your current directory

```sh
tfversion use --required
```

### List versions

```sh
Expand Down
35 changes: 28 additions & 7 deletions cmd/install.go
Expand Up @@ -19,37 +19,57 @@ const (
"\n" +
"\n" +
"# Install the latest pre-release Terraform version\n" +
"tfversion install --latest --pre-release"
"tfversion install --latest --pre-release\n" +
"\n" +
"\n" +
"# Install the required Terraform version for your current directory\n" +
"tfversion install --required"
)

var (
latest bool
preRelease bool
required bool
installCmd = &cobra.Command{
Use: "install",
Short: "Installs a given Terraform version",
Example: installExample,
PreRun: func(cmd *cobra.Command, args []string) {
if preRelease && !latest {
cmd.MarkFlagRequired("latest")
_ = cmd.MarkFlagRequired("latest")
}
},
Run: func(cmd *cobra.Command, args []string) {

// install latest
if latest {
if len(args) != 0 {
fmt.Println("error: `--latest` flag does not require specifying a Terraform version")
fmt.Printf("See %s for help and examples\n", color.BlueString("`tfversion install -h`"))
os.Exit(1)
}
install.InstallVersion("", latest, preRelease)
} else {
if len(args) != 1 {
fmt.Println("error: provide a Terraform version to install")
install.InstallLatestVersion(preRelease)
os.Exit(0)
}

// installed required version
if required {
if len(args) != 0 {
fmt.Println("error: `--required` flag does not require specifying a Terraform version")
fmt.Printf("See %s for help and examples\n", color.BlueString("`tfversion install -h`"))
os.Exit(1)
}
install.InstallVersion(args[0], latest, preRelease)
install.InstallRequiredVersion()
os.Exit(0)
}

// install specific version
if len(args) != 1 {
fmt.Println("error: provide a Terraform version to install")
fmt.Printf("See %s for help and examples\n", color.BlueString("`tfversion install -h`"))
os.Exit(1)
}
install.InstallVersion(args[0])
},
}
)
Expand All @@ -58,4 +78,5 @@ func init() {
rootCmd.AddCommand(installCmd)
installCmd.Flags().BoolVar(&latest, "latest", false, "install the latest stable Terraform version")
installCmd.Flags().BoolVar(&preRelease, "pre-release", false, "When used with --latest, install the latest pre-release version")
installCmd.Flags().BoolVar(&required, "required", false, "When used with --required, install the minimum required version for the current module")
}
34 changes: 27 additions & 7 deletions cmd/use.go
Expand Up @@ -19,7 +19,11 @@ const (
"\n" +
"\n" +
"# Use the latest pre-release Terraform version\n" +
"tfversion use --latest --pre-release"
"tfversion use --latest --pre-release\n" +
"\n" +
"\n" +
"# Use the required Terraform version for your current directory\n" +
"tfversion use --required"
)

var (
Expand All @@ -29,25 +33,40 @@ var (
Example: useExample,
PreRun: func(cmd *cobra.Command, args []string) {
if preRelease && !latest {
cmd.MarkFlagRequired("latest")
_ = cmd.MarkFlagRequired("latest")
}
},
Run: func(cmd *cobra.Command, args []string) {

// use latest version
if latest {
if len(args) != 0 {
fmt.Println("error: `--latest` flag does not require specifying a Terraform version")
fmt.Printf("See %s for help and examples\n", color.BlueString("`tfversion install -h`"))
os.Exit(1)
}
use.UseVersion("", latest, preRelease)
} else {
if len(args) != 1 {
fmt.Println("error: provide a Terraform version to activate")
use.UseLatestVersion(preRelease)
os.Exit(0)
}

// use required version
if required {
if len(args) != 0 {
fmt.Println("error: `--required` flag does not require specifying a Terraform version")
fmt.Printf("See %s for help and examples\n", color.BlueString("`tfversion install -h`"))
os.Exit(1)
}
use.UseVersion(args[0], latest, preRelease)
use.UseRequiredVersion()
os.Exit(0)
}

// use specific version
if len(args) != 1 {
fmt.Println("error: provide a Terraform version to activate")
fmt.Printf("See %s for help and examples\n", color.BlueString("`tfversion install -h`"))
os.Exit(1)
}
use.UseVersion(args[0])
},
}
)
Expand All @@ -56,4 +75,5 @@ func init() {
rootCmd.AddCommand(useCmd)
useCmd.Flags().BoolVar(&latest, "latest", false, "use the latest stable Terraform version")
useCmd.Flags().BoolVar(&preRelease, "pre-release", false, "When used with --latest, use the latest pre-release version")
useCmd.Flags().BoolVar(&required, "required", false, "use the required Terraform version for your current directory")
}
1 change: 1 addition & 0 deletions go.mod
Expand Up @@ -4,6 +4,7 @@ go 1.21.4

require (
github.com/fatih/color v1.16.0
github.com/hashicorp/go-version v1.6.0
github.com/spf13/cobra v1.8.0
golang.org/x/net v0.21.0
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
@@ -1,6 +1,8 @@
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
Expand Down
54 changes: 53 additions & 1 deletion pkg/helpers/helper.go
@@ -1,8 +1,60 @@
package helpers

import "strings"
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/hashicorp/go-version"
)

// IsPreReleaseVersion checks if the given version is a Terraform pre-release version
func IsPreReleaseVersion(version string) bool {
return strings.Contains(version, "-alpha") || strings.Contains(version, "-beta") || strings.Contains(version, "-rc")
}

// FindRequiredVersionInFile finds the required Terraform version in a given .tf file (using required_version = ">= x.x.x")
func FindRequiredVersionInFile(filepath string, availableVersions []string) string {
bytes, err := os.ReadFile(filepath)
if err != nil {
fmt.Printf("Unable to find version number in file: %s", filepath)
return ""
}

re := regexp.MustCompile(`required_version\s?=\s?"([^"]+)"`)
match := re.FindStringSubmatch(string(bytes))
if len(match) == 0 {
return ""
}

for _, v := range availableVersions {
testVersion, _ := version.NewVersion(v)
constraints, _ := version.NewConstraint(match[1])
if constraints.Check(testVersion) {
return v
}
}

return ""
}

// FindTerraformFiles finds all .tf files in the current directory (module)
func FindTerraformFiles() []string {
var files []string
err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if strings.HasSuffix(path, ".tf") {
files = append(files, path)
}
return nil
})
if err != nil {
fmt.Println("No Terraform files found in current directory:", err)
os.Exit(1)
}
return files
}
50 changes: 33 additions & 17 deletions pkg/install/install.go
Expand Up @@ -12,27 +12,12 @@ import (
)

// InstallVersion installs the specified Terraform version or one of the latest versions
func InstallVersion(version string, latest bool, preRelease bool) {
// Get the available Terraform versions
versions := list.GetAvailableVersions()

// Set the version to the latest stable version if the `latest` flag is set
// or to the latest pre-release version if the `latest` and `pre-release` flags are set
if latest {
for _, v := range versions {
if !preRelease && helpers.IsPreReleaseVersion(v) {
continue
}
version = v
break
}
}

func InstallVersion(version string) {
if download.IsAlreadyDownloaded(version) {
if helpers.IsPreReleaseVersion(version) {
fmt.Printf("Terraform version %s is already installed\n", color.YellowString(version))
} else {
fmt.Printf("Terraform version %s downloaded successfully\n", color.BlueString(version))
fmt.Printf("Terraform version %s is already installed\n", color.BlueString(version))
}
os.Exit(0)
}
Expand All @@ -59,3 +44,34 @@ func InstallVersion(version string, latest bool, preRelease bool) {
os.Exit(1)
}
}

// InstallLatestVersion installs the latest Terraform version
func InstallLatestVersion(preRelease bool) {
version := list.FindLatestVersion(preRelease)
InstallVersion(version)
}

// InstallRequiredVersion installs the required Terraform version from the .tf files in the current directory
func InstallRequiredVersion() {
terraformFiles := helpers.FindTerraformFiles()
if len(terraformFiles) == 0 {
fmt.Println("error: no Terraform files found in current directory")
os.Exit(1)
}

var foundVersion string
availableVersions := list.GetAvailableVersions()
for _, file := range terraformFiles {
requiredVersion := helpers.FindRequiredVersionInFile(file, availableVersions)
if requiredVersion != "" {
foundVersion = requiredVersion
}
}

if len(foundVersion) == 0 {
fmt.Println("error: no required version found in current directory")
os.Exit(1)
}

InstallVersion(foundVersion)
}
18 changes: 18 additions & 0 deletions pkg/list/list.go
Expand Up @@ -7,6 +7,7 @@ import (
"strings"

"github.com/tfversion/tfversion/pkg/download"
"github.com/tfversion/tfversion/pkg/helpers"
"golang.org/x/net/html"
)

Expand Down Expand Up @@ -75,3 +76,20 @@ func parseAvailableVersions(n *html.Node) []string {

return availableVersions
}

// FindLatestVersion finds the latest available Terraform version (or pre-release version)
func FindLatestVersion(preRelease bool) string {
versions := GetAvailableVersions()
var foundVersion string
for _, v := range versions {
if !preRelease && helpers.IsPreReleaseVersion(v) {
continue
}
foundVersion = v
}
if foundVersion == "" {
fmt.Println("No versions found")
os.Exit(1)
}
return foundVersion
}
41 changes: 25 additions & 16 deletions pkg/use/use.go
Expand Up @@ -13,22 +13,7 @@ import (
)

// UseVersion activates the specified Terraform version or one of the latest versions
func UseVersion(version string, latest bool, preRelease bool) {
// Get the available Terraform versions
versions := list.GetAvailableVersions()

// Set the version to the latest stable version if the `latest` flag is set
// or to the latest pre-release version if the `latest` and `pre-release` flags are set
if latest {
for _, v := range versions {
if !preRelease && helpers.IsPreReleaseVersion(v) {
continue
}
version = v
break
}
}

func UseVersion(version string) {
if !download.IsAlreadyDownloaded(version) {
if helpers.IsPreReleaseVersion(version) {
fmt.Printf("Terraform version %s not found, run %s to install\n", color.YellowString(version), color.BlueString(fmt.Sprintf("`tfversion install %s`", version)))
Expand Down Expand Up @@ -83,3 +68,27 @@ func UseVersion(version string, latest bool, preRelease bool) {
fmt.Printf("Activated Terraform version %s\n", color.BlueString(version))
}
}

// UseLatestVersion activates the latest Terraform version
func UseLatestVersion(preRelease bool) {
version := list.FindLatestVersion(preRelease)
UseVersion(version)
}

// UseRequiredVersion activates the required Terraform version from the .tf files in the current directory
func UseRequiredVersion() {
terraformFiles := helpers.FindTerraformFiles()
if len(terraformFiles) == 0 {
fmt.Println("No Terraform files found in current directory")
os.Exit(1)
}

availableVersions := list.GetAvailableVersions()
for _, file := range terraformFiles {
requiredVersion := helpers.FindRequiredVersionInFile(file, availableVersions)
if requiredVersion != "" {
UseVersion(requiredVersion)
break
}
}
}

0 comments on commit 0b90ebf

Please sign in to comment.