diff --git a/command/build.go b/command/build.go index 05392c063..6765b3128 100644 --- a/command/build.go +++ b/command/build.go @@ -104,7 +104,7 @@ func (c *BuildCommand) GetBuildsFromHCL(path string) ([]packer.Build, int) { PostProcessorsSchemas: c.CoreConfig.Components.PostProcessorStore, } - builds, diags := parser.Parse(path, c.varFiles, c.flagVars, c.CoreConfig.Only, c.CoreConfig.Except) + cfg, diags := parser.Parse(path, c.varFiles, c.flagVars) { // write HCL errors/diagnostics if any. b := bytes.NewBuffer(nil) @@ -122,6 +122,24 @@ func (c *BuildCommand) GetBuildsFromHCL(path string) ([]packer.Build, int) { ret = 1 } + builds, diags := cfg.GetBuilds(c.CoreConfig.Only, c.CoreConfig.Except) + { + // write HCL errors/diagnostics if any. + b := bytes.NewBuffer(nil) + err := hcl.NewDiagnosticTextWriter(b, parser.Files(), 80, false).WriteDiagnostics(diags) + if err != nil { + c.Ui.Error("could not write diagnostic: " + err.Error()) + return nil, 1 + } + if b.Len() != 0 { + c.Ui.Message(b.String()) + } + } + ret = 0 + if diags.HasErrors() { + ret = 1 + } + return builds, ret } diff --git a/hcl2template/common_test.go b/hcl2template/common_test.go index d0a88a6ee..b64295057 100644 --- a/hcl2template/common_test.go +++ b/hcl2template/common_test.go @@ -59,7 +59,7 @@ func testParse(t *testing.T, tests []parseTest) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gotCfg, gotDiags := tt.parser.parse(tt.args.filename, tt.args.varFiles, tt.args.vars) + gotCfg, gotDiags := tt.parser.Parse(tt.args.filename, tt.args.varFiles, tt.args.vars) if tt.parseWantDiags == (gotDiags == nil) { t.Fatalf("Parser.parse() unexpected %q diagnostics.", gotDiags) } @@ -116,7 +116,7 @@ func testParse(t *testing.T, tests []parseTest) { return } - gotBuilds, gotDiags := gotCfg.getBuilds(nil, nil) + gotBuilds, gotDiags := gotCfg.GetBuilds(nil, nil) if tt.getBuildsWantDiags == (gotDiags == nil) { t.Fatalf("Parser.getBuilds() unexpected diagnostics. %s", gotDiags) } diff --git a/hcl2template/parser.go b/hcl2template/parser.go index f621f32dc..169d336f3 100644 --- a/hcl2template/parser.go +++ b/hcl2template/parser.go @@ -52,7 +52,11 @@ const ( hcl2VarJsonFileExt = ".auto.pkrvars.json" ) -func (p *Parser) parse(filename string, varFiles []string, argVars map[string]string) (*PackerConfig, hcl.Diagnostics) { +// Parse will Parse HCL file(s) in path. Path can be a folder or a file. +// +// Parse will first Parse variables and then the rest; so that interpolation +// can happen. +func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]string) (*PackerConfig, hcl.Diagnostics) { var files []*hcl.File var diags hcl.Diagnostics diff --git a/hcl2template/types.packer_config.go b/hcl2template/types.packer_config.go index 940defe92..86b3702cc 100644 --- a/hcl2template/types.packer_config.go +++ b/hcl2template/types.packer_config.go @@ -7,8 +7,6 @@ import ( "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/packer" "github.com/zclconf/go-cty/cty" - - "github.com/gobwas/glob" ) // PackerConfig represents a loaded Packer HCL config. It will contain @@ -253,10 +251,10 @@ func (cfg *PackerConfig) getCoreBuildPostProcessors(source *SourceBlock, blocks return res, diags } -// getBuilds will return a list of packer Build based on the HCL2 parsed build +// GetBuilds returns a list of packer Build based on the HCL2 parsed build // blocks. All Builders, Provisioners and Post Processors will be started and // configured. -func (cfg *PackerConfig) getBuilds(onlyGlobs []glob.Glob, exceptGlobs []glob.Glob) ([]packer.Build, hcl.Diagnostics) { +func (cfg *PackerConfig) GetBuilds(only, except []string) ([]packer.Build, hcl.Diagnostics) { res := []packer.Build{} var diags hcl.Diagnostics @@ -276,7 +274,11 @@ func (cfg *PackerConfig) getBuilds(onlyGlobs []glob.Glob, exceptGlobs []glob.Glo buildName := fmt.Sprintf("%s.%s", src.Type, src.Name) // -only - if len(onlyGlobs) > 0 { + if len(only) > 0 { + onlyGlobs, diags := convertFilterOption(only, "only") + if diags.HasErrors() { + return nil, diags + } include := false for _, onlyGlob := range onlyGlobs { if onlyGlob.Match(buildName) { @@ -290,7 +292,11 @@ func (cfg *PackerConfig) getBuilds(onlyGlobs []glob.Glob, exceptGlobs []glob.Glo } // -except - if len(exceptGlobs) > 0 { + if len(except) > 0 { + exceptGlobs, diags := convertFilterOption(except, "except") + if diags.HasErrors() { + return nil, diags + } exclude := false for _, exceptGlob := range exceptGlobs { if exceptGlob.Match(buildName) { @@ -356,60 +362,3 @@ func (cfg *PackerConfig) getBuilds(onlyGlobs []glob.Glob, exceptGlobs []glob.Glo } return res, diags } - -// Convert -only and -except globs to glob.Glob instances. -func convertFilterOption(patterns []string, optionName string) ([]glob.Glob, hcl.Diagnostics) { - var globs []glob.Glob - var diags hcl.Diagnostics - - for _, pattern := range patterns { - g, err := glob.Compile(pattern) - if err != nil { - diags = append(diags, &hcl.Diagnostic{ - Summary: fmt.Sprintf("Invalid -%s pattern %s: %s", optionName, pattern, err), - Severity: hcl.DiagError, - }) - } - globs = append(globs, g) - } - - return globs, diags -} - -// Parse will parse HCL file(s) in path. Path can be a folder or a file. -// -// Parse will first parse variables and then the rest; so that interpolation -// can happen. -// -// For each build block a packer.Build will be started, and for each builder, -// all provisioners and post-processors will be started. -// -// Parse then return a slice of packer.Builds; which are what packer core uses -// to run builds. -func (p *Parser) Parse(path string, varFiles []string, argVars map[string]string, onlyBuilds []string, exceptBuilds []string) ([]packer.Build, hcl.Diagnostics) { - var onlyGlobs []glob.Glob - if len(onlyBuilds) > 0 { - og, diags := convertFilterOption(onlyBuilds, "only") - if diags.HasErrors() { - return nil, diags - } - onlyGlobs = og - } - - var exceptGlobs []glob.Glob - if len(exceptBuilds) > 0 { - eg, diags := convertFilterOption(exceptBuilds, "except") - if diags.HasErrors() { - return nil, diags - } - exceptGlobs = eg - } - - cfg, diags := p.parse(path, varFiles, argVars) - if diags.HasErrors() { - return nil, diags - } - - builds, moreDiags := cfg.getBuilds(onlyGlobs, exceptGlobs) - return builds, append(diags, moreDiags...) -} diff --git a/hcl2template/utils.go b/hcl2template/utils.go index bbb5d697b..06aa60864 100644 --- a/hcl2template/utils.go +++ b/hcl2template/utils.go @@ -1,11 +1,13 @@ package hcl2template import ( + "fmt" "io/ioutil" "os" "path/filepath" "strings" + "github.com/gobwas/glob" "github.com/hashicorp/hcl/v2" ) @@ -88,3 +90,22 @@ func GetHCL2Files(filename, hclSuffix, jsonSuffix string) (hclFiles, jsonFiles [ return hclFiles, jsonFiles, diags } + +// Convert -only and -except globs to glob.Glob instances. +func convertFilterOption(patterns []string, optionName string) ([]glob.Glob, hcl.Diagnostics) { + var globs []glob.Glob + var diags hcl.Diagnostics + + for _, pattern := range patterns { + g, err := glob.Compile(pattern) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Summary: fmt.Sprintf("Invalid -%s pattern %s: %s", optionName, pattern, err), + Severity: hcl.DiagError, + }) + } + globs = append(globs, g) + } + + return globs, diags +}