Skip to content

Commit

Permalink
Merge pull request #1 from cloudreach/pre-commit-hook
Browse files Browse the repository at this point in the history
feat: Add config to be used as pre-commit hook
  • Loading branch information
kcarros authored Apr 12, 2022
2 parents 5ffad84 + f8e8590 commit 29d4c66
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 28 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Compiled stuff
/bin
terraform-versions*
dist/

# Golang
.idea/

23 changes: 23 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
repos:
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.5.0
hooks:
- id: go-fmt
- id: go-vet
- id: go-lint
- id: go-imports
- id: go-cyclo
args: [-over=15]
- id: validate-toml
- id: no-go-testing
- id: golangci-lint
- id: go-critic
- id: go-unit-tests
- id: go-build
- id: go-mod-tidy
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: no-commit-to-branch
7 changes: 7 additions & 0 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- id: terraform-provider-version-check
name: terraform-provider-version-check
description: Check versions of terraform providers
language: system
pass_filenames: false
entry: tfpvc
types: [terraform]
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ release-mac-applesilicon:
tar -C bin -czvf bin/$(BINARY_NAME).osx-arm64.tar.gz $(BINARY_NAME)
git checkout -- ./cmd/version.go

all: build-all
all: build-all
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ A utility to check whether Terraform providers configured in a project are up-to
## Usage

```shell
tfpvc-osx-amd64 --help
tfpvc help
A utility to check whether Terraform providers configured in a project are up-to-date.

* Reads .terraform.lock.hcl file to get details of providers used in a project
Expand All @@ -29,11 +29,24 @@ Available Commands:

Flags:
--errorOnUpdate Exit with error code if updates are available
--findLockFiles Search for lockfiles in tfDir (default true)
-h, --help help for tfpvc
--tfDir string Directory with TF Files (default ".")

```
## pre-commit hook
With pre-commit, you can ensure you are notified of updates to your Terraform provider config each time you make a commit.
First install `pre-commit` and then create or update a `.pre-commit-config.yaml` in the root of your Git repo with at least the following content:
```yaml
repos:
- repo: https://github.com/cloudreach/tf-provider-version-check
rev: "1.0.0"
hooks:
- id: terraform-provder-version-check
```
## Requirements
* Terraform must be installed and available on the system PATH
Expand All @@ -56,4 +69,4 @@ make install-mac-intel

# OSX/arm64
make install-mac-applesilicon
```
```
97 changes: 73 additions & 24 deletions cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,97 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-exec/tfexec"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"os/user"
"path/filepath"
"strings"
"time"

mapset "github.com/deckarep/golang-set"
"github.com/fatih/color"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-exec/tfexec"
)

func check() {
if !lockFileExists(tfDir) {
fmt.Println("No .terraform.lock.hcl found. Exiting")
os.Exit(1)
func findDirsWithFiles() []string {
var toReturn []string

var searchPath string
if strings.HasPrefix(tfDir, "~/") {
usr, _ := user.Current()
homeDir := usr.HomeDir

searchPath = filepath.Join(homeDir, tfDir[2:])
} else {
searchPath = tfDir
}

execPath, err := exec.LookPath("terraform")
err := filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
log.Fatal(err)
return nil
}

if !info.IsDir() && info.Name() == ".terraform.lock.hcl" {
toReturn = append(toReturn, filepath.Dir(path))
}

return nil
})
if err != nil {
fmt.Println("Terraform executable not found on path, install terraform")
os.Exit(1)
log.Fatal(err)
}

tf, err := tfexec.NewTerraform(tfDir, execPath)
if err != nil {
fmt.Printf("Error accessing Terraform: %s\n", err)
os.Exit(1)
return toReturn
}

func check() {
dirSet := mapset.NewSet()

if findLockFiles {
for _, dir := range findDirsWithFiles() {
dirSet.Add(dir)
}
} else {
if !lockFileExists(tfDir) {
fmt.Println("No .terraform.lock.hcl found. Exiting")
os.Exit(1)
}
dirSet.Add(tfDir)
}

_, providerVersions, err := tf.Version(context.Background(), true)
execPath, err := exec.LookPath("terraform")
if err != nil {
fmt.Printf("Error running terraform version: %s\n", err)
fmt.Println("Terraform executable not found on path, install terraform")
os.Exit(1)
}

updatesAvailable := false
for provider, version := range providerVersions {
updates := checkVersion(provider, version)
if updates {
updatesAvailable = true

dirIterator := dirSet.Iterator()
for dir := range dirIterator.C {
fmt.Println("Found lockfile in: " + dir.(string))
tf, err := tfexec.NewTerraform(dir.(string), execPath)
if err != nil {
fmt.Printf("Error accessing Terraform: %s\n", err)
os.Exit(1)
}

_, providerVersions, err := tf.Version(context.Background(), true)
if err != nil {
fmt.Printf("Error running terraform version: %s\n", err)
os.Exit(1)
}

for provider, version := range providerVersions {
updates := checkVersion(provider, version)
if updates {
updatesAvailable = true
}
}
}

Expand All @@ -56,6 +108,7 @@ func check() {
os.Exit(0)
}

// RegistryResponse represents the JSON return by the HC Registry as a struct
type RegistryResponse struct {
ID string `json:"id"`
Owner string `json:"owner"`
Expand Down Expand Up @@ -93,30 +146,26 @@ func checkVersion(provider string, localVersion *version.Version) bool {
resp, err := http.Get("https://registry.terraform.io/v1/providers/" + providerName)
if err != nil {
fmt.Printf("Couldn't get provider details from HC: %s\n", err)
os.Exit(1)
}

defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Couldn't get read response body: %s\n", err)
os.Exit(1)
}

var response RegistryResponse
if err := json.Unmarshal(body, &response); err != nil {
fmt.Printf("Could not unmarshal response JSON: %s\n", err)
os.Exit(1)
}

remoteVersion, err := version.NewVersion(response.Version)
if err != nil {
fmt.Printf("Could not parse remote version: %s\n", err)
os.Exit(1)
}

if localVersion.LessThan(remoteVersion) {
fmt.Println("Update of", providerName, "available", localVersion, "<", remoteVersion)
fmt.Println("Update of", color.HiYellowString(providerName), "available", color.RedString(localVersion.String()), "<", color.BlueString(remoteVersion.String()))
return true
}

Expand Down
3 changes: 3 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

var tfDir string
var errorOnUpdate bool
var findLockFiles bool

var rootCmd = &cobra.Command{
Use: "tfpvc",
Expand All @@ -22,6 +23,7 @@ var rootCmd = &cobra.Command{
},
}

// Execute the check command
func Execute() {
cobra.CheckErr(rootCmd.Execute())
}
Expand All @@ -30,6 +32,7 @@ func init() {
cobra.OnInitialize(initConfig)

rootCmd.PersistentFlags().StringVar(&tfDir, "tfDir", ".", "Directory with TF Files")
rootCmd.PersistentFlags().BoolVar(&findLockFiles, "findLockFiles", true, "Search for lockfiles in tfDir")
rootCmd.PersistentFlags().BoolVar(&errorOnUpdate, "errorOnUpdate", false, "Exit with error code if updates are available")
}

Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module tf-provider-version-check
go 1.17

require (
github.com/deckarep/golang-set v1.8.0
github.com/fatih/color v1.13.0
github.com/hashicorp/go-version v1.4.0
github.com/hashicorp/terraform-exec v0.16.0
github.com/spf13/cobra v1.4.0
Expand All @@ -15,6 +17,8 @@ require (
github.com/hashicorp/terraform-json v0.13.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/spf13/afero v1.6.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
Expand All @@ -118,6 +120,7 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
Expand Down Expand Up @@ -299,12 +302,14 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
Expand Down

0 comments on commit 29d4c66

Please sign in to comment.