Skip to content

Commit

Permalink
pre-color: fix issue that wasn't able to parse output that already ha…
Browse files Browse the repository at this point in the history
…d been colored (#4)

* pre-color: fix issue that wasn't able to parse output that already had been colored

* readme improvements

* change here not needed

* simplify example of markdown in readme

* more direct help to using docker

* cleanup test
  • Loading branch information
jeff-knurek authored Sep 22, 2020
1 parent 1ac6c08 commit 32cb7ae
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 54 deletions.
59 changes: 24 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,18 @@
# tfarbe
Add color to Terraform 12 plan output.

Inspired from: https://github.com/coinbase/terraform-landscape

Add color to Terraform 12 plan output.
**NOTE**: if you're using terraform v11, tfarbe will not help and you should use `terraform-landscape`.

**NOTE**: if you're using terraform <=11, this tool will not help and you should use terraform-landscape.
### Example of output

<img src="./example/screenshot.png" width="65%" alt="Improved Terraform plan output" />

Also formats the ouput for markdown diff.

For example, this:
```
# module.apps.kubernetes_service.service will be updated in-place
~ resource "kubernetes_service" "service" {
id = "some-app"
load_balancer_ingress = []
~ metadata {
annotations = {}
generation = 0
~ labels = {
"app.kubernetes.io/managed-by" = "terraform"
~ "app.kubernetes.io/version" = "latest" -> "1.0.1"
}
# module.apps.kubernetes_deployment.deployment is tainted, so must be replaced
-/+ resource "kubernetes_deployment" "deployment" {
~ id = "some-app" -> (known after apply)
Expand All @@ -45,19 +34,6 @@ For example, this:
becomes:

```diff
# module.apps.kubernetes_service.service will be updated in-place
~ resource "kubernetes_service" "service" {
id = "some-app"
load_balancer_ingress = []

~ metadata {
annotations = {}
generation = 0
~ labels = {
"app.kubernetes.io/managed-by" = "terraform"
~ "app.kubernetes.io/version" = "latest" -> "1.0.1"
}

# module.apps.kubernetes_deployment.deployment is tainted, so must be replaced
-/+ resource "kubernetes_deployment" "deployment" {
~ id = "some-app" -> (known after apply)
Expand All @@ -78,23 +54,36 @@ becomes:

## Install

Binaries (should) be available on the [Release page](/releases) for both Linux and Mac. You can simple copy one of these binaries to your PATH.

At the moment it's only been tested in Ubuntu with a small selection of output.
Binaries (should) be available on the [Release page](https://github.com/jeff-knurek/tfarbe/releases/) for both Linux and Mac. You can simple copy one of these binaries to your PATH.

## Usage

due to https://github.com/jeff-knurek/tfarbe/issues/3 need to pass `-no-color` arg as a workaround. This should be resolved soon

```
terraform plan -no-color | tfarbe
terraform plan ... | tfarbe
```

### with Docker

```
git clone https://github.com/jeff-knurek/tfarbe.git
cd tfarbe
docker build . -t tfarbe
terraform plan -no-color ... | docker run -i --rm tfarbe
....
terraform plan ... | docker run -i --rm tfarbe
```

### Helpful bash addition

Add this to your `.bash_profile`/`.bashrc`/`...` accordingly

```
terraform() {
if [[ $1 == "plan" ]]; then
command terraform "$@" | docker run --rm -i tfarbe
else
command terraform "$@"
fi
}
```

## License
Expand Down
19 changes: 14 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"regexp"
"strings"

. "github.com/logrusorgru/aurora"
Expand All @@ -25,17 +26,19 @@ func iterateInput(input io.Reader) {

// process in-coming text
func processLine(raw string, out io.Writer) {
trimmed := strings.TrimSpace(raw)
var toPrint interface{}
cleaned := cleanRawInput(raw)

trimmed := strings.TrimSpace(cleaned)
if len(trimmed) < 1 {
fmt.Fprintln(out, raw)
return
}

firstChar := string(trimmed[0])
var toPrint interface{}
switch firstChar {
case "~":
ch := strings.Replace(raw, "~", "", 1)
ch := strings.Replace(cleaned, "~", "", 1)
sp := strings.SplitAfter(ch, " -> ")
if len(sp) != 2 {
toPrint = Yellow("~" + ch)
Expand All @@ -51,10 +54,10 @@ func processLine(raw string, out io.Writer) {
old := sp2[1]
toPrint = fmt.Sprintf("%s%s%s", Yellow(ch), Red(old), Green(new))
case "+":
new := strings.Replace(raw, "+", "", 1)
new := strings.Replace(cleaned, "+", "", 1)
toPrint = Green("+" + new)
case "-":
new := strings.Replace(raw, "-", "", 1)
new := strings.Replace(cleaned, "-", "", 1)
toPrint = Red("-" + new)
case "#":
toPrint = trimmed
Expand All @@ -64,3 +67,9 @@ func processLine(raw string, out io.Writer) {
fmt.Fprintln(out, toPrint)
return
}

func cleanRawInput(raw string) string {
ansi := "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
re := regexp.MustCompile(ansi)
return re.ReplaceAllString(raw, "")
}
58 changes: 44 additions & 14 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,83 +7,80 @@ import (

func Test_processLine(t *testing.T) {
tests := []struct {
name string
text string
out *bytes.Buffer
want string
wantErr bool
name string
text string
out *bytes.Buffer
want string
}{
{
name: "empty",
text: "",
out: &bytes.Buffer{},
want: `
`,
wantErr: false,
},
{
name: "whitespace",
text: " ",
out: &bytes.Buffer{},
want: " " + `
`,
wantErr: false,
},
{
name: "new resource",
text: " # module.example ",
out: &bytes.Buffer{},
want: `# module.example
`,
wantErr: false,
},
{
name: "no change",
text: " id = \"some string\"",
out: &bytes.Buffer{},
want: ` id = "some string"
`,
wantErr: false,
},
{
name: "added line",
text: " + id = \"some string\"",
out: &bytes.Buffer{},
want: `+ id = "some string"
`,
wantErr: false,
},
{
name: "removed line",
text: " - item = 0 -> null",
out: &bytes.Buffer{},
want: `- item = 0 -> null
`,
wantErr: false,
},
{
name: "changed line",
text: " ~ \"new/version\" = \"latest\" -> \"1.0.1\"",
out: &bytes.Buffer{},
want: `~ "new/version" = "latest" -> "1.0.1"
`,
wantErr: false,
},
{
name: "complex changed line",
text: " ~ \"new/version = some -> thing\" = \"latest\" -> \"1.0.1\"",
out: &bytes.Buffer{},
want: `~ "new/version = some -> thing" = "latest" -> "1.0.1"
`,
wantErr: false,
},
{
name: "cannot edit in place",
text: " - item = 0 -> null",
out: &bytes.Buffer{},
want: `- item = 0 -> null
`,
wantErr: false,
},
{
name: "pre-existing color",
text: " + id = \"some string\"",
out: &bytes.Buffer{},
want: `+ id = "some string"
`,
},
}
for _, tt := range tests {
Expand All @@ -97,3 +94,36 @@ func Test_processLine(t *testing.T) {
})
}
}

func Test_cleanRawInput(t *testing.T) {
type args struct {
}
tests := []struct {
name string
raw string
want string
}{
{
name: "only whitespace",
raw: " ",
want: " ",
},
{
name: "no color with space",
raw: " id = \"some string\" ",
want: " id = \"some string\" ",
},
{
name: "partial colored",
raw: " + id = \"some string\"",
want: " + id = \"some string\"",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := cleanRawInput(tt.raw); got != tt.want {
t.Errorf("cleanRawInput(%s) \ngot: %v, \nwant: %v", tt.name, got, tt.want)
}
})
}
}

0 comments on commit 32cb7ae

Please sign in to comment.