Skip to content

Commit

Permalink
Replace --glob option with --recursive
Browse files Browse the repository at this point in the history
  • Loading branch information
jpnauta committed Jul 20, 2024
1 parent 057bf5e commit 8c69ea5
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 40 deletions.
20 changes: 7 additions & 13 deletions cli/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import (
type GenerateCommand struct {
fs *flag.FlagSet

file string
glob string
check bool
file string
recursive bool
check bool
}

// NewGenerateCommand sub-command to generate files
Expand All @@ -20,8 +20,8 @@ func NewGenerateCommand() *GenerateCommand {
fs: flag.NewFlagSet("generate", flag.ContinueOnError),
}

c.fs.StringVar(&c.file, "file", "", "path to generator file")
c.fs.StringVar(&c.glob, "glob", "", "glob used to find generator files")
c.fs.StringVar(&c.file, "file", "tf-generator.hcl", "path to generator file")
c.fs.BoolVar(&c.recursive, "recursive", false, "search recursively for all files match generator file name")
c.fs.BoolVar(&c.check, "check", false, "only check if file is up-to-date, do not update it")

return c
Expand All @@ -43,15 +43,9 @@ func (c *GenerateCommand) Init(args []string) error {
}

func (c *GenerateCommand) Run() error {
if c.glob != "" && c.file != "" {
return fmt.Errorf("cannot specify --file and --glob")
}
if c.glob != "" {
return generate.RunGlob(c.glob, c.check)
if c.recursive {
return generate.RunRecursive(c.file, c.check)
} else {
if c.file == "" {
c.file = "tf-generator.hcl"
}
return generate.Run(c.file, c.check)
}
}
2 changes: 1 addition & 1 deletion default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ final: prev:

src = ./.;

vendorHash = "sha256-TIDg83EHqgzL9QOOsgGPm6f50qvnFbZPHwVo6pBaPQQ=";
vendorHash = "sha256-InRf1tpAVo2x6IFUmqERfRcT9P0qAd3lEvZ1u5Nfbn4=";

meta = {
description = "Simple generator for Terraform/OpenTofu";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
invalid {
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
generate {
content = merge-tfvars([
load("locals.tfvars"),
])
output = "tf-generator.tfvars"
}
12 changes: 8 additions & 4 deletions generate/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ package generate

import (
"fmt"

"github.com/yargevad/filepathx"
"os"
)

// Run main entry point for the `generate` command
Expand Down Expand Up @@ -37,8 +36,13 @@ func Run(filePath string, check bool) error {
return nil
}

func RunGlob(glob string, check bool) error {
files, err := filepathx.Glob(glob)
func RunRecursive(filename string, check bool) error {
if hasDirs(filename) {
return fmt.Errorf("file name %s cannot contain folders", filename)
}

cwd, _ := os.Getwd()
files, err := findFilesByName(cwd, filename)
if err != nil {
return err
}
Expand Down
35 changes: 35 additions & 0 deletions generate/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package generate

import (
"os"
"path/filepath"
)

// findFilesByName finds all files with a specific name in a directory and its subdirectories
func findFilesByName(directory, filename string) ([]string, error) {
var matches []string

err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if info.Name() == filename {
matches = append(matches, path)
}
return nil
})

if err != nil {
return nil, err
}
return matches, nil
}

// hasDirs check if the file path specifies any directories
func hasDirs(filePath string) bool {
dir, _ := filepath.Split(filePath)
return dir != ""
}
37 changes: 37 additions & 0 deletions generate/util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package generate

import (
"testing"

"github.com/stretchr/testify/assert"
)

type HasDirsFixture struct {
filePath string
hasDirs bool
}

func TestHasDirsFixtures(t *testing.T) {
for _, fixture := range []HasDirsFixture{
{
filePath: "test.txt",
hasDirs: false,
},
{
filePath: "abc",
hasDirs: false,
},
{
filePath: "./test.txt",
hasDirs: true,
},
{
filePath: "test/test.txt",
hasDirs: true,
},
} {
t.Run(fixture.filePath, func(t *testing.T) {
assert.Equal(t, fixture.hasDirs, hasDirs(fixture.filePath))
})
}
}
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ require (
github.com/google/go-cmp v0.5.8 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/yargevad/filepathx v1.0.0 // indirect
golang.org/x/text v0.11.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NF
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc=
github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA=
github.com/zclconf/go-cty v1.14.2 h1:kTG7lqmBou0Zkx35r6HJHUQTvaRPr5bIAf3AoHS0izI=
github.com/zclconf/go-cty v1.14.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
Expand Down
91 changes: 73 additions & 18 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,131 +11,186 @@ import (
)

type ValidFixture struct {
cwd string
args []string
}

type InvalidFixture struct {
cwd string
args []string
expectedMessageContains string
isDiag bool
}

func withCwd(t *testing.T, newDir string, fn func()) {
// Save current working directory
oldDir, err := os.Getwd()
if err != nil {
t.Fatalf("error getting current working directory: %v", err)
}

// Change cwd temporarily
if err := os.Chdir(newDir); err != nil {
t.Fatalf("error changing directory to %s: %v", newDir, err)
}

fn()

// Restore original cwd
if err := os.Chdir(oldDir); err != nil {
t.Fatalf("error restoring original directory: %v", err)
}
}

func TestValidFixtures(t *testing.T) {
for _, fixture := range []ValidFixture{
{
cwd: ".",
args: []string{"--file", "examples/basics-01-load-and-combine/tf-generator.hcl"},
},
{
cwd: ".",
args: []string{"--file", "examples/basics-02-locals/tf-generator.hcl"},
},
{
cwd: ".",
args: []string{"--file", "examples/basics-03-merge-tfvars/tf-generator.hcl"},
},
{
cwd: ".",
args: []string{"--file", "examples/basics-04-remove-tfvar-keys/tf-generator.hcl"},
},
{
cwd: ".",
args: []string{"--file", "examples/basics-05-combine-with-inject/tf-generator.hcl"},
},
{
cwd: ".",
args: []string{"--file", "fixtures/valid/empty/tf-generator.hcl"},
},
{
cwd: ".",
args: []string{"--file", "fixtures/valid/empty-tfvars/tf-generator.hcl"},
},
{
cwd: ".",
args: []string{"--file", "fixtures/valid/duplicate-includes/tf-generator.hcl"},
},
{
cwd: ".",
args: []string{"--file", "fixtures/valid/include-file-comments/tf-generator.hcl"},
},
{
cwd: ".",
args: []string{"--file", "fixtures/valid/locals-referencing-locals/tf-generator.hcl"},
},
{
args: []string{"--glob", "fixtures/valid/*/tf-generator.hcl"},
cwd: "fixtures/valid/",
args: []string{"--recursive"},
},
} {
t.Run(strings.Join(fixture.args, " "), func(t *testing.T) {
args := append([]string{"generate", "--check"}, fixture.args...)
if err := run(args); err != nil {
t.Fatal(err)
}
withCwd(t, fixture.cwd, func() {
args := append([]string{"generate", "--check"}, fixture.args...)
if err := run(args); err != nil {
t.Fatal(err)
}
})
})
}
}

func TestInvalidFixtures(t *testing.T) {
for _, fixture := range []InvalidFixture{
{
cwd: ".",
args: []string{"--file", "fixtures/invalid/unknown-block/tf-generator.hcl"},
expectedMessageContains: "Unsupported block type; Blocks of type \"unknown\" are not expected here.",
isDiag: true,
},
{
cwd: ".",
args: []string{"--file", "fixtures/invalid/syntax-error/tf-generator.hcl"},
expectedMessageContains: "Unclosed configuration block; There is no closing brace for this block before the end of the file.",
isDiag: true,
},
{
cwd: ".",
args: []string{"--file", "fixtures/invalid/unknown/tf-generator.hcl"},
expectedMessageContains: "<nil>: Configuration file not found; The configuration file fixtures/invalid/unknown/tf-generator.hcl does not exist.",
isDiag: true,
},
{
cwd: ".",
args: []string{"--file", "fixtures/invalid/tfvars-does-not-exist/tf-generator.hcl"},
expectedMessageContains: "no such file or directory.",
isDiag: true,
},
{
cwd: ".",
args: []string{"--file", "fixtures/invalid/empty-generated-tfvars/tf-generator.hcl"},
expectedMessageContains: "the new tfvars file does not match the existing file.",
isDiag: false,
},
{
cwd: ".",
args: []string{"--file", "fixtures/invalid/invalid-tfvars/tf-generator.hcl"},
expectedMessageContains: "parse config: [locals.tfvars:1,9-10: Unclosed configuration block; There is no closing brace for this block",
isDiag: true,
},
{
cwd: ".",
args: []string{"--file", "fixtures/invalid/injected-tfvar-does-not-exist/tf-generator.hcl"},
expectedMessageContains: "Could not find tfvar for ref `injectvar.does-not-exist`",
isDiag: true,
},
{
cwd: ".",
args: []string{"--file", "fixtures/invalid/duplicate-local/tf-generator.hcl"},
expectedMessageContains: `tf-generator.hcl:6,3-8: local "a" already defined`,
isDiag: true,
},
{
args: []string{"--glob", "fixtures/invalid/*/tf-generator.hcl"},
cwd: ".",
args: []string{"--file", "a/tf-generator.hcl", "--recursive"},
expectedMessageContains: `file name a/tf-generator.hcl cannot contain folders`,
isDiag: false,
},
{
cwd: "fixtures/invalid/",
args: []string{"--recursive"},
expectedMessageContains: `local "a" already defined`,
isDiag: true,
},
{
args: []string{"--glob", "fixtures/**/tf-generator.hcl"},
expectedMessageContains: `local "a" already defined`,
cwd: "fixtures/invalid/nested-folder-with-error",
args: []string{"--recursive"},
expectedMessageContains: "parse config: [locals.tfvars:1,9-10: Unclosed configuration block; There is no closing brace for this block",
isDiag: true,
},
{
args: []string{"--file", "test", "--glob", "test"},
expectedMessageContains: "cannot specify --file and --glob",
isDiag: false,
cwd: "fixtures/",
args: []string{"--recursive"},
expectedMessageContains: `local "a" already defined`,
isDiag: true,
},
{
cwd: ".",
args: []string{},
expectedMessageContains: "The configuration file tf-generator.hcl does not exist.",
isDiag: true,
},
} {
t.Run(strings.Join(fixture.args, " "), func(t *testing.T) {
args := append([]string{"generate", "--check"}, fixture.args...)
err := run(args)
assert.NotNilf(t, err, "expected error")
errMsg := err.Error()
assert.Contains(t, errMsg, fixture.expectedMessageContains)
_, isDiag := err.(hcl.Diagnostics)
assert.Equal(t, fixture.isDiag, isDiag)
withCwd(t, fixture.cwd, func() {
args := append([]string{"generate", "--check"}, fixture.args...)
err := run(args)
assert.NotNilf(t, err, "expected error")
errMsg := err.Error()
assert.Contains(t, errMsg, fixture.expectedMessageContains)
_, isDiag := err.(hcl.Diagnostics)
assert.Equal(t, fixture.isDiag, isDiag)
})
})
}
}
Expand Down
4 changes: 3 additions & 1 deletion nix-test.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#!/usr/bin/env bash
set -e

nix-build -E 'let tf-generator-overlay = import ./.; pkgs = import <nixpkgs> { overlays = [ tf-generator-overlay ]; }; in pkgs.mkShellNoCC { buildInputs = with pkgs; [ tf-generator ]; }' --dry-run
mkdir -p dist
nix-build -E 'let tf-generator-overlay = import ./.; pkgs = import <nixpkgs> { overlays = [ tf-generator-overlay ]; }; in pkgs.mkShellNoCC { buildInputs = with pkgs; [ tf-generator ]; }' -o dist/release
rm -rf dist

0 comments on commit 8c69ea5

Please sign in to comment.