Skip to content

Commit

Permalink
feat: allow configuration of diff display via -difftool option (#14)
Browse files Browse the repository at this point in the history
* attempt experimental windows builds, leave disabled by default
* difftool implementation
* difftool tests
* difftool chdir before diff for shorter path display
* test and document alternate difftool
* update badges w/ release info and links
  • Loading branch information
rhenning authored Jun 4, 2021
1 parent 54a97b6 commit 3424f52
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 54 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ $(DISTDIR)/$(TARGET): $(DISTDIR) $(SOURCES) test

clean:
$(GOCLEAN) $(VERBOSE)
$(GOCLEAN) $(VERBOSE) -modcache
$(GOCLEAN) $(VERBOSE) -testcache
rm -rf $(DISTDIR)/

tools:
Expand Down
46 changes: 42 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# terrajux
[![build-status](https://img.shields.io/github/workflow/status/rhenning/terrajux/test/main?style=for-the-badge)](https://github.com/rhenning/terrajux/actions/workflows/test.yml?query=workflow%3Atest+branch%3Amain)
[![current-release](https://img.shields.io/github/release/rhenning/terrajux.svg?style=for-the-badge)](https://github.com/rhenning/terrajux/releases/latest)
[![semantic-release-info](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=for-the-badge)](https://github.com/semantic-release/semantic-release)
[![license-info](https://img.shields.io/github/license/rhenning/terrajux?style=for-the-badge)](https://www.apache.org/licenses/LICENSE-2.0)

[![License](https://img.shields.io/github/license/rhenning/terrajux?style=for-the-badge)](LICENSE)
[![Release](https://img.shields.io/github/release/rhenning/terrajux.svg?style=for-the-badge)](https://github.com/rhenning/terrajux/releases/latest)
![Build](https://img.shields.io/github/workflow/status/rhenning/terrajux/test/main?style=for-the-badge)
<!--
[![Build status](https://img.shields.io/github/workflow/rhenning/terrajux/build?style=for-the-badge)](https://github.com/rhenning/terrajux/actions?workflow=build)
[![Codecov branch](https://img.shields.io/codecov/c/github/rhenning/terrajux/main.svg?style=for-the-badge)](https://codecov.io/gh/rhenning/terrajux)
[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg?style=for-the-badge)](http://godoc.org/github.com/rhenning/terrajux)
-->
Expand Down Expand Up @@ -120,3 +120,41 @@ in short, `terrajux`:
- macos and linux builds are fully tested for every pr and release
- *bsd and solaris builds are untested but _should_ work
- windows builds are disabled pending [some portability issues](https://github.com/rhenning/terrajux/issues)


> how can i use ________ to view the diff?
try the `-difftool` option.

`-difftool` accepts a [go template](https://golang.org/pkg/text/template/) that
can be used to format commands for an alternative diff viewer. the strings
`{{.V1}}` and `{{.V2}}` will be replaced by the `v1ref` and `v2ref` args
supplied to `terrajux` at runtime.

to use the [compare folders plugin](https://marketplace.visualstudio.com/items?itemName=moshfeu.compare-folders)
in [vs code](https://code.visualstudio.com/), for example, try:

```
-difftool 'COMPARE_FOLDERS=DIFF code {{.V1}} {{.V2}}'
```

to avoid typing this every time, consider creating an alias in
[your shell's profile](https://en.wikipedia.org/wiki/Unix_shell#Configuration_files),
such as:

```
alias terrajux="terrajux -difftool 'opendiff {{.V1}} {{.V2}}'"
```


> i'm seeing a stale diff for a branch ref or getting strange errors during
initialization. what gives?

try clearing the cache with the `-clearcache` option.

`terrajux` keeps a local cache of checkouts and initialized terraform modules
to speed up subsequent diffs of long-lived release tags. the cache key is a
concatentation of git url (sans scheme) and ref. when diffing dynamic refs
such as branches (or a tag that has been deleted and repointed), the cache entry
may be stale. you'll know this is the case if output displays something like
`Found <repo>@<ref> in cache. Skipping clone.`
3 changes: 3 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ type Config struct {
Name string
Version string
ProjectURL string

DataDir string
CacheDir string
CacheClear bool
DiffTool string

GitURL string
GitRefV1 string
GitRefV2 string
Expand Down
15 changes: 11 additions & 4 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ func (a *App) Run() (err error) {
}
}

if err = os.Chdir(a.Config.CacheDir); err != nil {
return err
}

for i, v := range []string{a.Config.GitRefV1, a.Config.GitRefV2} {
dir := filepath.Join(a.Config.CacheDir, git.URLPath(a.Config.GitURL, v))
dir := git.URLPath(a.Config.GitURL, v)
clonedirs[i] = dir

if _, err := os.Stat(dir); os.IsNotExist(err) {
Expand All @@ -54,7 +58,7 @@ func (a *App) Run() (err error) {
return err
}
} else {
fmt.Printf("Found %v @%v in cache. Skipping clone.\n", a.Config.GitURL, v)
fmt.Printf("Found %v@%v in cache. Skipping clone.\n", a.Config.GitURL, v)
}
}

Expand All @@ -81,8 +85,11 @@ func NewDefaultWiring(config *terrajux.Config) (app *App, err error) {
app.Terraform = terraform.NewCLI()
app.Cache = cache.New(config.CacheDir)

dr, err := diff.NewRunner(&diff.RunnerOptions{})
app.Diff = *dr
difftool, err := diff.NewTool(&diff.ToolOptions{
CommandTemplate: config.DiffTool,
})

app.Diff = difftool

return app, err
}
11 changes: 7 additions & 4 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package cli

// Usage: terrajux [options] <giturl> <v1ref> <v2ref> [subpath]
// Usage: terrajux [-help|-version] [-datadir d] [-clear] giturl v1ref v2ref [subpath]

import (
"bytes"
"flag"
Expand Down Expand Up @@ -71,8 +68,14 @@ func (c *CLI) ParseArgs() (message string, err error) {

flags.SetOutput(&buf)

flags.BoolVar(&c.Config.CacheClear, "clear", false, "clear cache")
flags.BoolVar(&c.Config.CacheClear, "clearcache", false, "purge the checkout and module cache")
flags.BoolVar(&showVersion, "version", false, "show version info")
flags.StringVar(
&c.Config.DiffTool,
"difftool",
"",
"diff command `template`, e.g. 'opendiff {{.V1}} {{.V2}}'",
)

err = flags.Parse(c.Args[1:])

Expand Down
25 changes: 23 additions & 2 deletions internal/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ func TestCLI_ParseArgs(t *testing.T) {
},
},
{
name: "clearflag+okargs+subpath",
name: "clearcacheflag+okargs+subpath",
fields: fields{
Args: []string{"terrajux", "-clear", "url", "v1", "v2", "sub/p"},
Args: []string{"terrajux", "-clearcache", "url", "v1", "v2", "sub/p"},
Config: &terrajux.Config{},
},

Expand All @@ -97,6 +97,27 @@ func TestCLI_ParseArgs(t *testing.T) {
GitSubpath: "sub/p",
},
},
{
name: "difftoolflag+okargs",
fields: fields{
Args: []string{
"terrajux",
"-difftool",
"opendiff {{.V1}} {{.V2}}",
"foo", "bar", "baz",
},
Config: &terrajux.Config{},
},

wantConfigValue: &terrajux.Config{
DiffTool: "opendiff {{.V1}} {{.V2}}",
CacheClear: false,
GitURL: "foo",
GitRefV1: "bar",
GitRefV2: "baz",
GitSubpath: "",
},
},
{
name: "badflag",
fields: fields{
Expand Down
42 changes: 26 additions & 16 deletions internal/diff/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"html/template"
"os"
"os/exec"
"path/filepath"
"strings"
)

Expand All @@ -25,57 +26,66 @@ var (
}
)

type RunnerOptions struct {
type ToolOptions struct {
Dir string
Shell string
CommandTemplate string
}

type Runner struct {
Options *RunnerOptions
type Tool struct {
Options *ToolOptions
ct *template.Template
command string
}

type Runner interface {
Run(v1path string, v2path string) error
}

type commandTemplateArgs struct {
V1 string
V2 string
}

func NewRunner(opts *RunnerOptions) (*Runner, error) {
runner := &Runner{
Options: &RunnerOptions{
func NewTool(opts *ToolOptions) (*Tool, error) {
tool := &Tool{
Options: &ToolOptions{
Dir: opts.Dir,
Shell: defaultShell,
CommandTemplate: strings.Join(defaultCommandTemplate, " "),
},
}

if opts.Shell != "" {
runner.Options.Shell = opts.Shell
tool.Options.Shell = opts.Shell
}

if opts.CommandTemplate != "" {
runner.Options.CommandTemplate = opts.CommandTemplate
tool.Options.CommandTemplate = opts.CommandTemplate
}

ct, err := template.New("diffcmd").Parse(runner.Options.CommandTemplate)
runner.ct = ct
ct, err := template.New("diffcmd").Parse(tool.Options.CommandTemplate)
tool.ct = ct

return runner, err
return tool, err
}

func (c *Runner) Run(v1path string, v2path string) error {
func (tool *Tool) Run(v1path string, v2path string) error {
for _, p := range []string{v1path, v2path} {
if _, err := os.Stat(p); err != nil {
dirp := filepath.Join(tool.Options.Dir, p)

if _, err := os.Stat(dirp); err != nil {
return err
}
}

if err := c.formatCommand(v1path, v2path); err != nil {
if err := tool.formatCommand(v1path, v2path); err != nil {
return err
}

cmd := exec.Command(c.Options.Shell, "-c", c.command)
cmd := exec.Command(tool.Options.Shell, "-c", tool.command)

cmd.Dir = tool.Options.Dir
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
Expand All @@ -91,7 +101,7 @@ func (c *Runner) Run(v1path string, v2path string) error {
return err
}

func (c *Runner) formatCommand(v1path string, v2path string) error {
func (c *Tool) formatCommand(v1path string, v2path string) error {
ctargs := commandTemplateArgs{v1path, v2path}
var command bytes.Buffer

Expand Down
Loading

0 comments on commit 3424f52

Please sign in to comment.