Skip to content

Commit

Permalink
feat: Add configuration to make releases
Browse files Browse the repository at this point in the history
  • Loading branch information
jsmithedin committed Apr 4, 2022
1 parent de79679 commit 5ffad84
Show file tree
Hide file tree
Showing 9 changed files with 994 additions and 141 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: release

on:
push:
tags:
- '*'

jobs:
build_release:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-go@v3
with:
go-version: '1.17'
- name: build artifacts
run: make release-linux && make release-mac-intel && make release-mac-applesilicon
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
bin/tfpvc.linux-amd64.tar.gz
bin/tfpvc.osx-amd64.tar.gz
bin/tfpvc.osx-arm64.tar.gz
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,25 @@ install-mac-intel:
install-mac-applesilicon:
cp bin/$(BINARY_NAME)-osx-arm64 /usr/local/bin/tfpvc

release-linux:
$(eval VERSION=$(shell git describe --tags --abbrev=0))
sed -i "s/LOCAL/$(VERSION)/g" ./cmd/version.go
GOOS=linux GOARCH=amd64 go build -o bin/$(BINARY_NAME) main.go
tar -C bin -czvf bin/$(BINARY_NAME).linux-amd64.tar.gz $(BINARY_NAME)
git checkout -- ./cmd/version.go

release-mac-intel:
$(eval VERSION=$(shell git describe --tags --abbrev=0))
sed -i "s/LOCAL/$(VERSION)/g" ./cmd/version.go
GOOS=darwin GOARCH=amd64 go build -o bin/$(BINARY_NAME) main.go
tar -C bin -czvf bin/$(BINARY_NAME).osx-amd64.tar.gz $(BINARY_NAME)
git checkout -- ./cmd/version.go

release-mac-applesilicon:
$(eval VERSION=$(shell git describe --tags --abbrev=0))
sed -i "s/LOCAL/$(VERSION)/g" ./cmd/version.go
GOOS=darwin GOARCH=arm64 go build -o bin/$(BINARY_NAME) main.go
tar -C bin -czvf bin/$(BINARY_NAME).osx-arm64.tar.gz $(BINARY_NAME)
git checkout -- ./cmd/version.go

all: build-all
30 changes: 23 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#Terraform Provider Version Check
# Terraform Provider Version Check

A utility to check whether Terraform providers configured in a project are up-to-date.

Expand All @@ -10,12 +10,28 @@ A utility to check whether Terraform providers configured in a project are up-to
## Usage

```shell
tfpvc -help
Usage of tfpvc:
-errorOnUpdate
Exit with error code if updates are available
-workingDir string
Directory with TF Files (default ".")
tfpvc-osx-amd64 --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
* Looks up registry.terraform.io to get the latest version for each
* If updates are available this is notified on STDOUT
* Can optionally return code on exit if update is available

Usage:
tfpvc [flags]
tfpvc [command]

Available Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
version Returns version data

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

```
## Requirements
Expand Down
124 changes: 124 additions & 0 deletions cmd/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package cmd

import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-exec/tfexec"
"io/ioutil"
"net/http"
"os"
"os/exec"
"strings"
"time"
)

func check() {
if !lockFileExists(tfDir) {
fmt.Println("No .terraform.lock.hcl found. Exiting")
os.Exit(1)
}

execPath, err := exec.LookPath("terraform")
if err != nil {
fmt.Println("Terraform executable not found on path, install terraform")
os.Exit(1)
}

tf, err := tfexec.NewTerraform(tfDir, 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)
}

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

if errorOnUpdate {
if updatesAvailable {
os.Exit(1)
}
}

os.Exit(0)
}

type RegistryResponse struct {
ID string `json:"id"`
Owner string `json:"owner"`
Namespace string `json:"namespace"`
Name string `json:"name"`
Alias string `json:"alias"`
Version string `json:"version"`
Tag string `json:"tag"`
Description string `json:"description"`
Source string `json:"source"`
PublishedAt time.Time `json:"published_at"`
Downloads int `json:"downloads"`
Tier string `json:"tier"`
LogoURL string `json:"logo_url"`
Versions []string `json:"versions"`
Docs []struct {
ID string `json:"id"`
Title string `json:"title"`
Path string `json:"path"`
Slug string `json:"slug"`
Category string `json:"category"`
Subcategory string `json:"subcategory"`
} `json:"docs"`
}

func lockFileExists(path string) bool {
lockFilePath := path + "/.terraform.lock.hcl"
_, err := os.Stat(lockFilePath)
return !errors.Is(err, os.ErrNotExist)
}

func checkVersion(provider string, localVersion *version.Version) bool {
providerName := strings.SplitN(provider, "/", 2)[1]

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)
return true
}

return false
}
38 changes: 38 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cmd

import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var tfDir string
var errorOnUpdate bool

var rootCmd = &cobra.Command{
Use: "tfpvc",
Short: "Terraform Provider Version Check",
Long: `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
* Looks up registry.terraform.io to get the latest version for each
* If updates are available this is notified on STDOUT
* Can optionally return code on exit if update is available`,
Run: func(cmd *cobra.Command, args []string) {
check()
},
}

func Execute() {
cobra.CheckErr(rootCmd.Execute())
}

func init() {
cobra.OnInitialize(initConfig)

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

func initConfig() {
viper.AutomaticEnv()
}
21 changes: 21 additions & 0 deletions cmd/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

const tfpvcVersion = "LOCAL"

var versionCmd = &cobra.Command{
Use: "version",
Short: "Returns version data",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(tfpvcVersion)
},
}

func init() {
rootCmd.AddCommand(versionCmd)
}
23 changes: 18 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
module terraform-versions
module tf-provider-version-check

go 1.17

require (
github.com/hashicorp/go-version v1.4.0
github.com/hashicorp/hc-install v0.3.1
github.com/hashicorp/terraform-exec v0.16.0
github.com/spf13/cobra v1.4.0
github.com/spf13/viper v1.10.1
)

require (
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
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/mitchellh/mapstructure v1.4.3 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/zclconf/go-cty v1.10.0 // indirect
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect
golang.org/x/text v0.3.5 // indirect
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Loading

0 comments on commit 5ffad84

Please sign in to comment.