mirror of
https://github.com/hashicorp/packer.git
synced 2026-02-19 02:28:50 -05:00
formatting multiple hcl files (#13362)
* formatting multiple hcl files * recursive formating * test case fixing * test case fix * removed not required code * existing test code fix * go formatting * more test cases for new cases * doc changes * added additional check on error message in negative test case
This commit is contained in:
parent
f16ca7fa03
commit
e8d5ddb38a
6 changed files with 122 additions and 49 deletions
|
|
@ -66,6 +66,7 @@ type MetaArgs struct {
|
|||
// TODO(azr): in the future, I want to allow passing multiple path to
|
||||
// merge HCL confs together; but this will probably need an RFC first.
|
||||
Path string
|
||||
Paths []string
|
||||
Only, Except []string
|
||||
Vars map[string]string
|
||||
VarFiles []string
|
||||
|
|
|
|||
|
|
@ -36,12 +36,12 @@ func (c *FormatCommand) ParseArgs(args []string) (*FormatArgs, int) {
|
|||
}
|
||||
|
||||
args = flags.Args()
|
||||
if len(args) != 1 {
|
||||
if len(args) == 0 {
|
||||
flags.Usage()
|
||||
return &cfg, 1
|
||||
}
|
||||
|
||||
cfg.Path = args[0]
|
||||
cfg.Paths = args
|
||||
return &cfg, 0
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ func (c *FormatCommand) RunContext(ctx context.Context, cla *FormatArgs) int {
|
|||
Recursive: cla.Recursive,
|
||||
}
|
||||
|
||||
bytesModified, diags := formatter.Format(cla.Path)
|
||||
bytesModified, diags := formatter.Format(cla.Paths)
|
||||
ret := writeDiags(c.Ui, nil, diags)
|
||||
if ret != 0 {
|
||||
return ret
|
||||
|
|
|
|||
|
|
@ -55,52 +55,53 @@ func (f *HCL2Formatter) formatFile(path string, diags hcl.Diagnostics, bytesModi
|
|||
// 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) {
|
||||
func (f *HCL2Formatter) Format(paths []string) (int, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
var bytesModified int
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
if s, err := os.Stat(path); err != nil || !s.IsDir() {
|
||||
return f.formatFile(path, diags, bytesModified)
|
||||
}
|
||||
for _, path := range paths {
|
||||
s, err := os.Stat(path)
|
||||
|
||||
fileInfos, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
diag := &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
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)
|
||||
if err != nil || !s.IsDir() {
|
||||
bytesModified, diags = f.formatFile(path, diags, bytesModified)
|
||||
} else {
|
||||
fileInfos, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
diag := &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Cannot read hcl directory",
|
||||
Detail: err.Error(),
|
||||
}
|
||||
diags = append(diags, diag)
|
||||
return bytesModified, diags
|
||||
}
|
||||
|
||||
for _, fileInfo := range fileInfos {
|
||||
name := fileInfo.Name()
|
||||
if f.shouldIgnoreFile(name) {
|
||||
continue
|
||||
}
|
||||
filename := filepath.Join(path, name)
|
||||
if fileInfo.IsDir() {
|
||||
if f.Recursive {
|
||||
var tempDiags hcl.Diagnostics
|
||||
var tempBytesModified int
|
||||
var newPaths []string
|
||||
newPaths = append(newPaths, filename)
|
||||
tempBytesModified, tempDiags = f.Format(newPaths)
|
||||
bytesModified += tempBytesModified
|
||||
diags = diags.Extend(tempDiags)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if isHcl2FileOrVarFile(filename) {
|
||||
bytesModified, diags = f.formatFile(filename, diags, bytesModified)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if isHcl2FileOrVarFile(filename) {
|
||||
bytesModified, diags = f.formatFile(filename, diags, bytesModified)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,6 +117,10 @@ func (f *HCL2Formatter) processFile(filename string) ([]byte, error) {
|
|||
f.Output = os.Stdout
|
||||
}
|
||||
|
||||
if !(filename == "-") && !isHcl2FileOrVarFile(filename) {
|
||||
return nil, fmt.Errorf("file %s is not a HCL file", filename)
|
||||
}
|
||||
|
||||
var in io.Reader
|
||||
var err error
|
||||
|
||||
|
|
@ -175,6 +180,14 @@ func (f *HCL2Formatter) processFile(filename string) ([]byte, error) {
|
|||
return outSrc, nil
|
||||
}
|
||||
|
||||
// shouldIgnoreFile returns true if the given filename (which must not have a
|
||||
// directory path ahead of it) should be ignored as e.g. an editor swap file.
|
||||
func (f *HCL2Formatter) shouldIgnoreFile(name string) bool {
|
||||
return strings.HasPrefix(name, ".") || // Unix-like hidden files
|
||||
strings.HasSuffix(name, "~") || // vim
|
||||
strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#") // emacs
|
||||
}
|
||||
|
||||
// bytesDiff returns the unified diff of b1 and b2
|
||||
// Shamelessly copied from Terraform's fmt command.
|
||||
func bytesDiff(b1, b2 []byte, path string) (data []byte, err error) {
|
||||
|
|
|
|||
|
|
@ -16,13 +16,15 @@ import (
|
|||
func TestHCL2Formatter_Format(t *testing.T) {
|
||||
tt := []struct {
|
||||
Name string
|
||||
Path string
|
||||
Paths []string
|
||||
FormatExpected bool
|
||||
}{
|
||||
{Name: "Unformatted file", Path: "testdata/format/unformatted.pkr.hcl", FormatExpected: true},
|
||||
{Name: "Unformatted vars file", Path: "testdata/format/unformatted.pkrvars.hcl", FormatExpected: true},
|
||||
{Name: "Formatted file", Path: "testdata/format/formatted.pkr.hcl"},
|
||||
{Name: "Directory", Path: "testdata/format", FormatExpected: true},
|
||||
{Name: "Unformatted file", Paths: []string{"testdata/format/unformatted.pkr.hcl"}, FormatExpected: true},
|
||||
{Name: "Unformatted vars file", Paths: []string{"testdata/format/unformatted.pkrvars.hcl"}, FormatExpected: true},
|
||||
{Name: "Formatted file", Paths: []string{"testdata/format/formatted.pkr.hcl"}},
|
||||
{Name: "Directory", Paths: []string{"testdata/format"}, FormatExpected: true},
|
||||
{Name: "No file", Paths: []string{}, FormatExpected: false},
|
||||
{Name: "Multi File", Paths: []string{"testdata/format/unformatted.pkr.hcl", "testdata/format/unformatted.pkrvars.hcl"}, FormatExpected: true},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
|
|
@ -30,12 +32,12 @@ func TestHCL2Formatter_Format(t *testing.T) {
|
|||
var buf bytes.Buffer
|
||||
f := NewHCL2Formatter()
|
||||
f.Output = &buf
|
||||
_, diags := f.Format(tc.Path)
|
||||
_, diags := f.Format(tc.Paths)
|
||||
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())
|
||||
t.Errorf("Format(%q) should contain the name of the formatted file(s), but got %q", tc.Paths, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +63,9 @@ func TestHCL2Formatter_Format_Write(t *testing.T) {
|
|||
_, _ = tf.Write(unformattedData)
|
||||
tf.Close()
|
||||
|
||||
_, diags := f.Format(tf.Name())
|
||||
var paths []string
|
||||
paths = append(paths, tf.Name())
|
||||
_, diags := f.Format(paths)
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("the call to Format failed unexpectedly %s", diags.Error())
|
||||
}
|
||||
|
|
@ -94,7 +98,9 @@ func TestHCL2Formatter_Format_ShowDiff(t *testing.T) {
|
|||
ShowDiff: true,
|
||||
}
|
||||
|
||||
_, diags := f.Format("testdata/format/unformatted.pkr.hcl")
|
||||
var paths []string
|
||||
paths = append(paths, "testdata/format/unformatted.pkr.hcl")
|
||||
_, diags := f.Format(paths)
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("the call to Format failed unexpectedly %s", diags.Error())
|
||||
}
|
||||
|
|
@ -109,3 +115,28 @@ func TestHCL2Formatter_Format_ShowDiff(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func TestHCL2Formatter_FormatNegativeCases(t *testing.T) {
|
||||
tt := []struct {
|
||||
Name string
|
||||
Paths []string
|
||||
errExpected bool
|
||||
}{
|
||||
{Name: "Unformatted file", Paths: []string{"testdata/format/test.json"}, errExpected: true},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
tc := tc
|
||||
var buf bytes.Buffer
|
||||
f := NewHCL2Formatter()
|
||||
f.Output = &buf
|
||||
_, diags := f.Format(tc.Paths)
|
||||
if tc.errExpected && !diags.HasErrors() {
|
||||
t.Fatalf("Expected error but got none")
|
||||
}
|
||||
|
||||
if diags[0].Detail != "file testdata/format/test.json is not a HCL file" {
|
||||
t.Fatalf("Expected error messge did not received")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
hcl2template/testdata/format/test.json
vendored
Normal file
19
hcl2template/testdata/format/test.json
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"builders": [
|
||||
{
|
||||
"type": "amazon-ebs",
|
||||
"access_key": "YOUR_AWS_ACCESS_KEY",
|
||||
"secret_key": "YOUR_AWS_SECRET_KEY",
|
||||
"region": "us-east-1"
|
||||
}
|
||||
],
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"sudo apt-get update",
|
||||
"sudo apt-get install -y nginx"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -32,6 +32,15 @@ my-template.pkr.hcl
|
|||
|
||||
```
|
||||
|
||||
Format multiple configuration files, writing the changes back to respective original files.
|
||||
|
||||
```shell-session
|
||||
$ packer fmt my-template.pkr.hcl my-varfile.pkrvars.hcl
|
||||
my-template.pkr.hcl
|
||||
my-varfile.pkrvars.hcl
|
||||
|
||||
```
|
||||
|
||||
Format a configuration file, reading from stdin and writing to stdout.
|
||||
|
||||
```shell-session
|
||||
|
|
|
|||
Loading…
Reference in a new issue