Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

RECENT to JSON Proposal #437

Merged
merged 19 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ require (
github.com/gookit/slog v0.5.5
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/hcl/v2 v2.20.1
github.com/hashicorp/terraform-config-inspect v0.0.0-20231204233900-a34142ec2a72
github.com/hashicorp/terraform-config-inspect v0.0.0-20240509232506-4708120f8f30
github.com/manifoldco/promptui v0.9.0
github.com/mitchellh/go-homedir v1.1.0
github.com/pborman/getopt v1.1.0
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.23.0
golang.org/x/sys v0.20.0
)
Expand All @@ -19,6 +20,7 @@ require (
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/gookit/color v1.5.4 // indirect
Expand All @@ -29,8 +31,9 @@ require (
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/locafero v0.5.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
Expand All @@ -41,11 +44,11 @@ require (
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/zclconf/go-cty v1.14.4 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/tools v0.20.0 // indirect
golang.org/x/tools v0.21.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31
github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
github.com/hashicorp/hcl/v2 v2.20.1 h1:M6hgdyz7HYt1UN9e61j+qKJBqR3orTWbI1HKBJEdxtc=
github.com/hashicorp/hcl/v2 v2.20.1/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4=
github.com/hashicorp/terraform-config-inspect v0.0.0-20231204233900-a34142ec2a72 h1:nZ5gGjbe5o7XUu1d7j+Y5Ztcxlp+yaumTKH9i0D3wlg=
github.com/hashicorp/terraform-config-inspect v0.0.0-20231204233900-a34142ec2a72/go.mod h1:l8HcFPm9cQh6Q0KSWoYPiePqMvRFenybP1CH2MjKdlg=
github.com/hashicorp/terraform-config-inspect v0.0.0-20240509232506-4708120f8f30 h1:0qwr2oZy9mIIJMWh7W9NTHLWGMbEF5KEQ+QqM9hym34=
github.com/hashicorp/terraform-config-inspect v0.0.0-20240509232506-4708120f8f30/go.mod h1:Gz/z9Hbn+4KSp8A2FBtNszfLSdT2Tn/uAKGuVqqWmDI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
Expand All @@ -65,8 +65,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/locafero v0.5.0 h1:zXz2JnQDgE5gDg0R9ThkNT0orQzm47i8IuO6hk6XSYY=
github.com/sagikazarmark/locafero v0.5.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
Expand Down Expand Up @@ -102,8 +102,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8=
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
Expand All @@ -116,8 +116,8 @@ golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
Expand Down
7 changes: 3 additions & 4 deletions lib/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ var (
PubKeyUri = "https://www.hashicorp.com/.well-known/pgp-key.txt"
)

const (
pubKeySuffix = ".asc"
)

// GetDefaultBin Get default binary path
func GetDefaultBin() string {
var defaultBin = "/usr/local/bin/terraform"
Expand All @@ -27,8 +23,11 @@ func GetDefaultBin() string {
const (
DefaultMirror = "https://releases.hashicorp.com/terraform"
DefaultLatest = ""
distributionTerraform = "terraform"
distributionOpenTofu = "opentofu"
installFile = "terraform"
InstallDir = ".terraform.versions"
pubKeySuffix = ".asc"
recentFile = "RECENT"
tfDarwinArm64StartVersion = "1.0.2"
VersionPrefix = "terraform_"
MatrixCrawler marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
104 changes: 8 additions & 96 deletions lib/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func install(tfversion string, binPath string, installPath string, mirrorURL str
/* set symlink to desired version */
CreateSymlink(installFileVersionPath, binPath)
logger.Infof("Switched terraform to version %q", tfversion)
addRecent(tfversion, installPath) //add to recent file for faster lookup
addRecent(tfversion, installPath, distributionTerraform) //add to recent file for faster lookup
return
}

Expand Down Expand Up @@ -134,99 +134,11 @@ func install(tfversion string, binPath string, installPath string, mirrorURL str
/* set symlink to desired version */
CreateSymlink(installFileVersionPath, binPath)
logger.Infof("Switched terraform to version %q", tfversion)
addRecent(tfversion, installPath) //add to recent file for faster lookup
addRecent(tfversion, installPath, distributionTerraform) //add to recent file for faster lookup
return
}

// addRecent : add to recent file
func addRecent(requestedVersion string, installPath string) {

installLocation = GetInstallLocation(installPath) //get installation location - this is where we will put our terraform binary file
versionFile := filepath.Join(installLocation, recentFile)

fileExist := CheckFileExist(versionFile)
if fileExist {
lines, errRead := ReadLines(versionFile)

if errRead != nil {
logger.Errorf("Error reading %q file: %v", versionFile, errRead)
return
}

for _, line := range lines {
if !validVersionFormat(line) {
logger.Infof("File %q is dirty (recreating cache file)", versionFile)
RemoveFiles(versionFile)
CreateRecentFile(requestedVersion, installPath)
return
}
}

versionExist := versionExist(requestedVersion, lines)

if !versionExist {
if len(lines) >= 3 {
_, lines = lines[len(lines)-1], lines[:len(lines)-1]

lines = append([]string{requestedVersion}, lines...)
_ = WriteLines(lines, versionFile)
} else {
lines = append([]string{requestedVersion}, lines...)
_ = WriteLines(lines, versionFile)
}
}

} else {
CreateRecentFile(requestedVersion, installPath)
}
}

// getRecentVersions : get recent version from file
func getRecentVersions(installPath string) ([]string, error) {

installLocation = GetInstallLocation(installPath) //get installation location - this is where we will put our terraform binary file
versionFile := filepath.Join(installLocation, recentFile)

fileExist := CheckFileExist(versionFile)
if fileExist {

lines, errRead := ReadLines(versionFile)
var outputRecent []string

if errRead != nil {
logger.Errorf("Error reading %q file: %f", versionFile, errRead)
return nil, errRead
}

for _, line := range lines {
/* checks if versions in the recent file are valid.
If any version is invalid, it will be considered dirty
and the recent file will be removed
*/
if !validVersionFormat(line) {
RemoveFiles(versionFile)
return nil, errRead
}

/* output can be confusing since it displays the 3 most recent used terraform version
append the string *recent to the output to make it more user friendly
*/
outputRecent = append(outputRecent, fmt.Sprintf("%s *recent", line))
}

return outputRecent, nil
}

return nil, nil
}

// CreateRecentFile : create RECENT file
func CreateRecentFile(requestedVersion string, installPath string) {
installLocation = GetInstallLocation(installPath) //get installation location - this is where we will put our terraform binary file
_ = WriteLines([]string{requestedVersion}, filepath.Join(installLocation, recentFile))
}

// ConvertExecutableExt : convert excutable with local OS extension
// ConvertExecutableExt : convert executable with local OS extension
func ConvertExecutableExt(fpath string) string {
switch runtime.GOOS {
case "windows":
Expand Down Expand Up @@ -314,7 +226,7 @@ func InstallVersion(dryRun bool, version, customBinaryPath, installPath, mirrorU
if recentDownloadFile {
ChangeSymlink(installFileVersionPath, customBinaryPath)
logger.Infof("Switched terraform to version %q", requestedVersion)
addRecent(requestedVersion, installPath) //add to recent file for faster lookup
addRecent(requestedVersion, installPath, distributionTerraform) //add to recent file for faster lookup
return
}

Expand All @@ -341,10 +253,10 @@ func InstallVersion(dryRun bool, version, customBinaryPath, installPath, mirrorU
/* listAll = true - all versions including beta and rc will be displayed */
/* listAll = false - only official stable release are displayed */
func InstallOption(listAll, dryRun bool, customBinaryPath, installPath string, mirrorURL string) {
tflist, _ := getTFList(mirrorURL, listAll) // Get list of versions
recentVersions, _ := getRecentVersions(installPath) // Get recent versions from RECENT file
tflist = append(recentVersions, tflist...) // Append recent versions to the top of the list
tflist = removeDuplicateVersions(tflist) // Remove duplicate version
tflist, _ := getTFList(mirrorURL, listAll) // Get list of versions
recentVersions, _ := getRecentVersions(installPath, distributionTerraform) // Get recent versions from RECENT file
tflist = append(recentVersions, tflist...) // Append recent versions to the top of the list
tflist = removeDuplicateVersions(tflist) // Remove duplicate version

if len(tflist) == 0 {
logger.Fatalf("Terraform version list is empty: %s", mirrorURL)
Expand Down
111 changes: 111 additions & 0 deletions lib/recent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package lib
MatrixCrawler marked this conversation as resolved.
Show resolved Hide resolved

import (
"encoding/json"
"os"
"path/filepath"
"strings"
)

type RecentFiles struct {
Terraform []string `json:"terraform"`
OpenTofu []string `json:"opentofu"`
}

func addRecent(requestedVersion string, installPath string, distribution string) {
if !validVersionFormat(requestedVersion) {
MatrixCrawler marked this conversation as resolved.
Show resolved Hide resolved
logger.Errorf("The version %q is not a valid version string and won't be stored", requestedVersion)
return
}
installLocation := GetInstallLocation(installPath)
recentFilePath := filepath.Join(installLocation, recentFile)
var recentFileData RecentFiles
if CheckFileExist(recentFilePath) {
unmarshalRecentFileData(recentFilePath, &recentFileData)
}
prependRecentVersionToList(requestedVersion, distribution, &recentFileData)
saveRecentFile(recentFileData, recentFilePath)
}

func prependRecentVersionToList(version, distribution string, r *RecentFiles) {
var sliceToCheck []string
switch distribution {
case distributionTerraform:
sliceToCheck = r.Terraform
case distributionOpenTofu:
sliceToCheck = r.OpenTofu
}
for versionIndex, versionValue := range sliceToCheck {
if versionValue == version {
sliceToCheck = append(sliceToCheck[:versionIndex], sliceToCheck[versionIndex+1:]...)
}
}
sliceToCheck = append([]string{version}, sliceToCheck...)

switch distribution {
case distributionTerraform:
r.Terraform = sliceToCheck
case distributionOpenTofu:
r.OpenTofu = sliceToCheck
}
}

func getRecentVersions(installPath string, dist string) ([]string, error) {
MatrixCrawler marked this conversation as resolved.
Show resolved Hide resolved
installLocation := GetInstallLocation(installPath)
recentFilePath := filepath.Join(installLocation, recentFile)
var recentFileData RecentFiles
unmarshalRecentFileData(recentFilePath, &recentFileData)
var listOfRecentVersions []string
switch dist {
case distributionTerraform:
listOfRecentVersions = recentFileData.Terraform
case distributionOpenTofu:
listOfRecentVersions = recentFileData.OpenTofu
}
var maxCount int
if len(listOfRecentVersions) >= 5 {
maxCount = 5
} else {
maxCount = len(listOfRecentVersions)
}
var returnedRecentVersions []string
for i := 0; i < maxCount; i++ {
returnedRecentVersions = append(returnedRecentVersions, listOfRecentVersions[i]+" *recent")
}
return returnedRecentVersions, nil
}

func unmarshalRecentFileData(recentFilePath string, recentFileData *RecentFiles) {
recentFileContent, err := os.ReadFile(recentFilePath)
if err != nil {
logger.Errorf("Could not open recent versions file %q", recentFilePath)
}
if string(recentFileContent[0:1]) != "{" {
convertOldRecentFile(recentFileContent, recentFileData)
} else {
err = json.Unmarshal(recentFileContent, &recentFileData)
if err != nil {
logger.Errorf("Could not unmarshal recent versions content from %q file", recentFilePath)
}
}
}

func convertOldRecentFile(content []byte, recentFileData *RecentFiles) {
MatrixCrawler marked this conversation as resolved.
Show resolved Hide resolved
lines := strings.Split(string(content), "\n")
for _, s := range lines {
if s != "" {
recentFileData.Terraform = append(recentFileData.Terraform, s)
}
}
}

func saveRecentFile(data RecentFiles, path string) {
bytes, err := json.Marshal(data)
if err != nil {
logger.Errorf("Could not marshal data to JSON: %v", err)
}
err = os.WriteFile(path, bytes, 0644)
if err != nil {
logger.Errorf("Could not save file %q: %v", path, err)
}
}
Loading