Adding recursive flag to formatter to format subdirectories

This commit is contained in:
teddylear 2021-01-08 21:22:26 -05:00
parent a5a1344948
commit ab4b3a8465
7 changed files with 256 additions and 45 deletions

View File

@ -147,12 +147,12 @@ func (va *FormatArgs) AddFlagSets(flags *flag.FlagSet) {
flags.BoolVar(&va.Check, "check", false, "check if the input is formatted")
flags.BoolVar(&va.Diff, "diff", false, "display the diff of formatting changes")
flags.BoolVar(&va.Write, "write", true, "overwrite source files instead of writing to stdout")
flags.BoolVar(&va.Recursive, "recursive", true, "Also process files in subdirectories")
va.MetaArgs.AddFlagSets(flags)
}
// FormatArgs represents a parsed cli line for `packer fmt`
type FormatArgs struct {
MetaArgs
Check, Diff, Write bool
Check, Diff, Write, Recursive bool
}

View File

@ -48,9 +48,10 @@ func (c *FormatCommand) RunContext(ctx context.Context, cla *FormatArgs) int {
}
formatter := hclutils.HCL2Formatter{
ShowDiff: cla.Diff,
Write: cla.Write,
Output: os.Stdout,
ShowDiff: cla.Diff,
Write: cla.Write,
Output: os.Stdout,
Recursive: cla.Recursive,
}
bytesModified, diags := formatter.Format(cla.Path)
@ -90,6 +91,8 @@ Options:
-write=false Don't write to source files
(always disabled if using -check)
-recursive Also process files in subdirectories. By default, only the
given directory (or current directory) is processed.
`
return strings.TrimSpace(helpText)
@ -105,8 +108,9 @@ func (*FormatCommand) AutocompleteArgs() complete.Predictor {
func (*FormatCommand) AutocompleteFlags() complete.Flags {
return complete.Flags{
"-check": complete.PredictNothing,
"-diff": complete.PredictNothing,
"-write": complete.PredictNothing,
"-check": complete.PredictNothing,
"-diff": complete.PredictNothing,
"-write": complete.PredictNothing,
"-recursive": complete.PredictNothing,
}
}

View File

@ -50,3 +50,15 @@ func TestFmt_unfomattedTemlateDirectory(t *testing.T) {
fatalCommand(t, c.Meta)
}
}
func TestFmt_Recursive(t *testing.T) {
c := &FormatCommand{
Meta: testMeta(t),
}
args := []string{"-check=true", "-recursive=true", filepath.Join(testFixture("fmt"), "")}
if code := c.Run(args); code != 3 {
fatalCommand(t, c.Meta)
}
}

View File

@ -7,6 +7,8 @@ import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclparse"
@ -14,9 +16,9 @@ import (
)
type HCL2Formatter struct {
ShowDiff, Write bool
Output io.Writer
parser *hclparse.Parser
ShowDiff, Write, Recursive bool
Output io.Writer
parser *hclparse.Parser
}
// NewHCL2Formatter creates a new formatter, ready to format configuration files.
@ -26,55 +28,88 @@ func NewHCL2Formatter() *HCL2Formatter {
}
}
func isHcl2FileOrVarFile(path string) bool {
if strings.HasSuffix(path, hcl2FileExt) || strings.HasSuffix(path, hcl2VarFileExt) {
return true
}
return false
}
func (f *HCL2Formatter) formatFile(path string, diags hcl.Diagnostics, bytesModified int) (int, hcl.Diagnostics) {
data, err := f.processFile(path)
if err != nil {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("encountered an error while formatting %s", path),
Detail: err.Error(),
})
}
bytesModified += len(data)
return bytesModified, diags
}
// Format all HCL2 files in path and return the total bytes formatted.
// If any error is encountered, zero bytes will be returned.
//
// Path can be a directory or a file.
func (f *HCL2Formatter) Format(path string) (int, hcl.Diagnostics) {
var diags hcl.Diagnostics
var bytesModified int
var allHclFiles []string
var diags []*hcl.Diagnostic
if path == "-" {
allHclFiles = []string{"-"}
} else {
hclFiles, _, diags := GetHCL2Files(path, hcl2FileExt, hcl2JsonFileExt)
if diags.HasErrors() {
return 0, diags
}
hclVarFiles, _, diags := GetHCL2Files(path, hcl2VarFileExt, hcl2VarJsonFileExt)
if diags.HasErrors() {
return 0, diags
}
allHclFiles = append(hclFiles, hclVarFiles...)
if len(allHclFiles) == 0 {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("Cannot tell whether %s contains HCL2 configuration data", path),
})
return 0, diags
}
if path == "" {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "path is empty, cannot format",
Detail: "path is empty, cannot format",
})
return bytesModified, diags
}
if f.parser == nil {
f.parser = hclparse.NewParser()
}
var bytesModified int
for _, fn := range allHclFiles {
data, err := f.processFile(fn)
isDir, err := isDir(path)
if err != nil {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Cannot tell wether " + path + " is a directory",
Detail: err.Error(),
})
return bytesModified, diags
}
if !isDir {
bytesModified, diags = f.formatFile(path, diags, bytesModified)
} else {
fileInfos, err := ioutil.ReadDir(path)
if err != nil {
diags = append(diags, &hcl.Diagnostic{
diag := &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: fmt.Sprintf("encountered an error while formatting %s", fn),
Summary: "Cannot read hcl directory",
Detail: err.Error(),
})
}
diags = append(diags, diag)
return bytesModified, diags
}
for _, fileInfo := range fileInfos {
filename := filepath.Join(path, fileInfo.Name())
if fileInfo.IsDir() {
if f.Recursive {
var tempDiags hcl.Diagnostics
var tempBytesModified int
tempBytesModified, tempDiags = f.Format(filename)
bytesModified += tempBytesModified
diags = diags.Extend(tempDiags)
} else {
continue
}
}
if isHcl2FileOrVarFile(filename) {
bytesModified, diags = f.formatFile(filename, diags, bytesModified)
}
}
bytesModified += len(data)
}
return bytesModified, diags
@ -84,6 +119,7 @@ func (f *HCL2Formatter) Format(path string) (int, hcl.Diagnostics) {
// overwriting the contents of the original when the f.Write is true; a diff of the changes
// will be outputted if f.ShowDiff is true.
func (f *HCL2Formatter) processFile(filename string) ([]byte, error) {
if f.Output == nil {
f.Output = os.Stdout
}

View File

@ -32,11 +32,18 @@ func TestHCL2Formatter_Format(t *testing.T) {
if diags.HasErrors() {
t.Fatalf("the call to Format failed unexpectedly %s", diags.Error())
}
if buf.String() != "" && tc.FormatExpected == false {
t.Errorf("Format(%q) should contain the name of the formatted file(s), but got %q", tc.Path, buf.String())
}
}
}
func TestHCL2Formatter_Recursive(t *testing.T) {
f := NewHCL2Formatter()
f.Recursive = true
_, diags := f.Format("testdata/format")
if diags.HasErrors() {
t.Fatalf("the call to Format failed unexpectedly %s", diags.Error())
}
}

View File

@ -0,0 +1,149 @@
// starts resources to provision them.
build {
sources = [
"source.amazon-ebs.ubuntu-1604",
"source.virtualbox-iso.ubuntu-1204",
]
provisioner "shell" {
string = coalesce(null, "", "string")
int = "${41 + 1}"
int64 = "${42 + 1}"
bool = "true"
trilean = true
duration = "${9 + 1}s"
map_string_string = {
a = "b"
c = "d"
}
slice_string = [
"a",
"b",
"c",
]
slice_slice_string = [
["a","b"],
["c","d"]
]
nested {
string = "string"
int = 42
int64 = 43
bool = true
trilean = true
duration = "10s"
map_string_string = {
a = "b"
c = "d"
}
slice_string = [
"a",
"b",
"c",
]
slice_slice_string = [
["a","b"],
["c","d"]
]
}
nested_slice {
}
}
provisioner "file" {
string = "string"
int = 42
int64 = 43
bool = true
trilean = true
duration = "10s"
map_string_string = {
a = "b"
c = "d"
}
slice_string = [
"a",
"b",
"c",
]
slice_slice_string = [
["a","b"],
["c","d"]
]
nested {
string = "string"
int = 42
int64 = 43
bool = true
trilean = true
duration = "10s"
map_string_string = {
a = "b"
c = "d"
}
slice_string = [
"a",
"b",
"c",
]
slice_slice_string = [
["a","b"],
["c","d"]
]
}
nested_slice {
}
}
post-processor "amazon-import" {
string = "string"
int = 42
int64 = 43
bool = true
trilean = true
duration = "10s"
map_string_string = {
a = "b"
c = "d"
}
slice_string = [
"a",
"b",
"c",
]
slice_slice_string = [
["a","b"],
["c","d"]
]
nested {
string = "string"
int = 42
int64 = 43
bool = true
trilean = true
duration = "10s"
map_string_string = {
a = "b"
c = "d"
}
slice_string = [
"a",
"b",
"c",
]
slice_slice_string = [
["a","b"],
["c","d"]
]
}
nested_slice {
}
}
}

View File

@ -0,0 +1,3 @@
ami_filter_name ="amzn2-ami-hvm-*-x86_64-gp2"
ami_filter_owners =[ "137112412989" ]