From 4aae59f23f3d5798028ac730d71957b536982aee Mon Sep 17 00:00:00 2001 From: Clayton Kehoe Date: Thu, 9 Apr 2026 09:42:24 -0500 Subject: [PATCH 1/8] feat: add SARIF region with startLine/startColumn for PR annotations Add ValidationError type with optional Line/Column fields to enable GitHub Actions inline annotations on affected lines. - 10 validators now return structured position info (JSON, YAML, TOML, XML, HCL, CSV, ENV, HOCON, TOON, PList) - 4 validators without position info (INI, EditorConfig, Properties, SARIF) fall back to file-level annotations - SARIF reporter emits region only when line info is available - Report struct carries StartLine/StartColumn from ValidationError - Remove dead getCustomErr function (logic moved into JSON ValidateSyntax) - Add GitHub Action section to README.md and index.md --- CHANGELOG.md | 10 ++++++ README.md | 10 ++++++ index.md | 10 ++++++ pkg/cli/cli.go | 9 ++++++ pkg/reporter/reporter.go | 2 ++ pkg/reporter/reporter_test.go | 54 +++++++++++++++++++++++++++++++++ pkg/reporter/sarif_reporter.go | 13 ++++++++ pkg/validator/csv.go | 8 +++++ pkg/validator/env.go | 6 ++-- pkg/validator/hcl.go | 6 +++- pkg/validator/hocon.go | 10 ++++++ pkg/validator/json.go | 26 ++++++++-------- pkg/validator/plist.go | 9 ++++++ pkg/validator/toml.go | 6 +++- pkg/validator/toon.go | 9 ++++++ pkg/validator/validator.go | 11 +++++++ pkg/validator/validator_test.go | 21 +++---------- pkg/validator/xml.go | 9 ++++++ pkg/validator/yaml.go | 9 ++++++ 19 files changed, 204 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c646c83b..4547451e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.0.1] - 2026-04-09 + +### Added + +- SARIF reporter now includes `region` with `startLine`/`startColumn` for inline PR annotations in GitHub Actions +- `ValidationError` type with optional `Line`/`Column` fields for structured error positions +- GitHub Action section in README and index referencing `Boeing/validate-configs-action@v2.0.0` + +## [2.0.0] - 2026-04-08 + ### Added - SARIF syntax and schema validation using the go-sarif library diff --git a/README.md b/README.md index 778e2728..7f6c191a 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,16 @@ If you have a go environment on your desktop you can use [go install](https://go go install github.com/Boeing/config-file-validator/cmd/validator@latest ``` +## GitHub Action + +A GitHub Action is available to run the config-file-validator as part of your CI/CD pipeline. It posts validation results as PR comments with inline annotations on the affected files and lines. + +```yaml +- uses: Boeing/validate-configs-action@v2.0.0 +``` + +See the [validate-configs-action](https://github.com/Boeing/validate-configs-action) repository for full usage and configuration options. + ## Usage ``` diff --git a/index.md b/index.md index d8426d51..14786ed5 100644 --- a/index.md +++ b/index.md @@ -140,6 +140,16 @@ If you have a go environment on your desktop you can use [go install](https://go go install github.com/Boeing/config-file-validator/cmd/validator@v1.8.1 ``` +## GitHub Action + +A GitHub Action is available to run the config-file-validator as part of your CI/CD pipeline. It posts validation results as PR comments with inline annotations on the affected files and lines. + +```yaml +- uses: Boeing/validate-configs-action@v2.0.0 +``` + +See the [validate-configs-action](https://github.com/Boeing/validate-configs-action) repository for full usage and configuration options. + ## Usage ``` diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go index 02a20f89..89ba8819 100644 --- a/pkg/cli/cli.go +++ b/pkg/cli/cli.go @@ -123,12 +123,21 @@ func (c CLI) Run() (int, error) { isValid, err = validateSchema(fileToValidate.FileType.Validator, fileContent, fileToValidate.Path) } + var line, col int + var ve *validator.ValidationError + if errors.As(err, &ve) { + line = ve.Line + col = ve.Column + } + report := reporter.Report{ FileName: fileToValidate.Name, FilePath: fileToValidate.Path, IsValid: isValid, ValidationError: err, IsQuiet: Quiet, + StartLine: line, + StartColumn: col, } if !isValid { errorFound = true diff --git a/pkg/reporter/reporter.go b/pkg/reporter/reporter.go index 6e7f3171..17266f04 100644 --- a/pkg/reporter/reporter.go +++ b/pkg/reporter/reporter.go @@ -8,6 +8,8 @@ type Report struct { IsValid bool ValidationError error IsQuiet bool + StartLine int + StartColumn int } // Reporter is the interface that wraps the Print method diff --git a/pkg/reporter/reporter_test.go b/pkg/reporter/reporter_test.go index 23b222c5..aaf8f671 100644 --- a/pkg/reporter/reporter_test.go +++ b/pkg/reporter/reporter_test.go @@ -1,6 +1,8 @@ package reporter import ( + "bytes" + "encoding/json" "errors" "os" "testing" @@ -17,6 +19,8 @@ var ( true, nil, false, + 0, + 0, } backslashReport = Report{ @@ -25,6 +29,8 @@ var ( true, nil, false, + 0, + 0, } invalidReport = Report{ @@ -33,6 +39,8 @@ var ( false, errors.New("unable to parse bad.xml file"), false, + 0, + 0, } multiLineErrorReport = Report{ @@ -41,6 +49,8 @@ var ( false, errors.New("unable to parse keys:\nkey1\nkey2"), false, + 0, + 0, } quietReport = Report{ @@ -49,6 +59,8 @@ var ( true, nil, true, + 0, + 0, } mixedReports = []Report{validReport, invalidReport, multiLineErrorReport} @@ -96,6 +108,8 @@ func Test_junitReport(t *testing.T) { false, errors.New("Incorrect characters '<' and '` found in file"), false, + 0, + 0, }} err := (JunitReporter{}).Print(reports) require.NoError(t, err) @@ -133,6 +147,44 @@ func Test_sarifReport(t *testing.T) { require.NoError(t, err) } +func Test_sarifReportWithRegion(t *testing.T) { + reportWithPos := Report{ + "bad.json", + "/fake/path/bad.json", + false, + errors.New("error at line 3 column 10"), + false, + 3, + 10, + } + reportLineOnly := Report{ + "bad.yaml", + "/fake/path/bad.yaml", + false, + errors.New("yaml: line 5: mapping error"), + false, + 5, + 0, + } + + var buf bytes.Buffer + log, err := createSARIFReport([]Report{reportWithPos, reportLineOnly, validReport}) + require.NoError(t, err) + + sarifBytes, err := json.MarshalIndent(log, "", " ") + require.NoError(t, err) + buf.Write(sarifBytes) + + output := buf.String() + // reportWithPos should have region with startLine and startColumn + assert.Contains(t, output, `"startLine": 3`) + assert.Contains(t, output, `"startColumn": 10`) + // reportLineOnly should have region with startLine only (no startColumn since it's 0) + assert.Contains(t, output, `"startLine": 5`) + // validReport should not have a region + assert.NotContains(t, output, `"startLine": 0`) +} + func Test_sarifReportToFile(t *testing.T) { tmpDir := t.TempDir() err := NewSARIFReporter(tmpDir).Print([]Report{validReport}) @@ -200,6 +252,8 @@ func Test_reporterFileOutput(t *testing.T) { true, nil, false, + 0, + 0, } for _, tc := range []struct { diff --git a/pkg/reporter/sarif_reporter.go b/pkg/reporter/sarif_reporter.go index e55d3e8e..c4b6cc9d 100644 --- a/pkg/reporter/sarif_reporter.go +++ b/pkg/reporter/sarif_reporter.go @@ -54,12 +54,18 @@ type location struct { type physicalLocation struct { ArtifactLocation artifactLocation `json:"artifactLocation"` + Region *region `json:"region,omitempty"` } type artifactLocation struct { URI string `json:"uri"` } +type region struct { + StartLine int `json:"startLine"` + StartColumn int `json:"startColumn,omitempty"` +} + func NewSARIFReporter(outputDest string) *SARIFReporter { return &SARIFReporter{ outputDest: outputDest, @@ -103,6 +109,13 @@ func createSARIFReport(reports []Report) (*SARIFLog, error) { location := &result.Locations[0] location.PhysicalLocation.ArtifactLocation.URI = "file:///" + report.FilePath + + if report.StartLine > 0 { + location.PhysicalLocation.Region = ®ion{ + StartLine: report.StartLine, + StartColumn: report.StartColumn, + } + } } return &log, nil diff --git a/pkg/validator/csv.go b/pkg/validator/csv.go index baf16336..e87094a5 100644 --- a/pkg/validator/csv.go +++ b/pkg/validator/csv.go @@ -26,6 +26,14 @@ func (CsvValidator) ValidateSyntax(b []byte) (bool, error) { } if err != nil { + var pe *csv.ParseError + if errors.As(err, &pe) { + return false, &ValidationError{ + Err: err, + Line: pe.Line, + Column: pe.Column, + } + } return false, err } } diff --git a/pkg/validator/env.go b/pkg/validator/env.go index 9032dea4..1037f42a 100644 --- a/pkg/validator/env.go +++ b/pkg/validator/env.go @@ -20,8 +20,10 @@ func (EnvValidator) ValidateSyntax(b []byte) (bool, error) { if err != nil { var customError *envparse.ParseError if errors.As(err, &customError) { - // we can wrap some useful information with the error - err = fmt.Errorf("error at line %v: %w", customError.Line, customError.Err) + return false, &ValidationError{ + Err: fmt.Errorf("error at line %v: %w", customError.Line, customError.Err), + Line: customError.Line, + } } return false, err } diff --git a/pkg/validator/hcl.go b/pkg/validator/hcl.go index d708b05c..5025f1c1 100644 --- a/pkg/validator/hcl.go +++ b/pkg/validator/hcl.go @@ -33,5 +33,9 @@ func (HclValidator) ValidateSyntax(b []byte) (bool, error) { row := subject.Start.Line col := subject.Start.Column - return false, fmt.Errorf("error at line %v column %v: %w", row, col, diags) + return false, &ValidationError{ + Err: fmt.Errorf("error at line %v column %v: %w", row, col, diags), + Line: row, + Column: col, + } } diff --git a/pkg/validator/hocon.go b/pkg/validator/hocon.go index 12069f71..65da0f2f 100644 --- a/pkg/validator/hocon.go +++ b/pkg/validator/hocon.go @@ -1,9 +1,14 @@ package validator import ( + "regexp" + "strconv" + "github.com/gurkankaymak/hocon" ) +var hoconLineColRe = regexp.MustCompile(`at: (\d+):(\d+)`) + // HoconValidator is used to validate a byte slice that is intended to represent a // HOCON file. type HoconValidator struct{} @@ -14,6 +19,11 @@ var _ Validator = HoconValidator{} func (HoconValidator) ValidateSyntax(b []byte) (bool, error) { _, err := hocon.ParseString(string(b)) if err != nil { + if m := hoconLineColRe.FindStringSubmatch(err.Error()); m != nil { + line, _ := strconv.Atoi(m[1]) + col, _ := strconv.Atoi(m[2]) + return false, &ValidationError{Err: err, Line: line, Column: col} + } return false, err } diff --git a/pkg/validator/json.go b/pkg/validator/json.go index eb393267..bdcc1b90 100644 --- a/pkg/validator/json.go +++ b/pkg/validator/json.go @@ -11,24 +11,22 @@ type JSONValidator struct{} var _ Validator = JSONValidator{} -func getCustomErr(input []byte, err error) error { - var jsonError *json.SyntaxError - if !errors.As(err, &jsonError) { - return err - } - - offset := int(jsonError.Offset) - line := 1 + strings.Count(string(input)[:offset], "\n") - column := 1 + offset - (strings.LastIndex(string(input)[:offset], "\n") + len("\n")) - return fmt.Errorf("error at line %v column %v: %w", line, column, jsonError) -} - func (JSONValidator) ValidateSyntax(b []byte) (bool, error) { var output any err := json.Unmarshal(b, &output) if err != nil { - customError := getCustomErr(b, err) - return false, customError + var synErr *json.SyntaxError + if errors.As(err, &synErr) { + offset := int(synErr.Offset) + line := 1 + strings.Count(string(b)[:offset], "\n") + column := 1 + offset - (strings.LastIndex(string(b)[:offset], "\n") + len("\n")) + return false, &ValidationError{ + Err: fmt.Errorf("error at line %v column %v: %w", line, column, synErr), + Line: line, + Column: column, + } + } + return false, err } return true, nil } diff --git a/pkg/validator/plist.go b/pkg/validator/plist.go index fa09b419..503f93ad 100644 --- a/pkg/validator/plist.go +++ b/pkg/validator/plist.go @@ -2,10 +2,14 @@ package validator import ( "bytes" + "regexp" + "strconv" "howett.net/plist" ) +var plistLineColRe = regexp.MustCompile(`at line (\d+) character (\d+)`) + // PlistValidator is used to validate a byte slice that is intended to represent a // Apple Property List file (plist). type PlistValidator struct{} @@ -18,6 +22,11 @@ func (PlistValidator) ValidateSyntax(b []byte) (bool, error) { plistDecoder := plist.NewDecoder(bytes.NewReader(b)) err := plistDecoder.Decode(&output) if err != nil { + if m := plistLineColRe.FindStringSubmatch(err.Error()); m != nil { + line, _ := strconv.Atoi(m[1]) + col, _ := strconv.Atoi(m[2]) + return false, &ValidationError{Err: err, Line: line, Column: col} + } return false, err } return true, nil diff --git a/pkg/validator/toml.go b/pkg/validator/toml.go index 522d6d33..54ef12dc 100644 --- a/pkg/validator/toml.go +++ b/pkg/validator/toml.go @@ -18,7 +18,11 @@ func (TomlValidator) ValidateSyntax(b []byte) (bool, error) { var derr *toml.DecodeError if errors.As(err, &derr) { row, col := derr.Position() - return false, fmt.Errorf("error at line %v column %v: %w", row, col, err) + return false, &ValidationError{ + Err: fmt.Errorf("error at line %v column %v: %w", row, col, err), + Line: row, + Column: col, + } } return true, nil } diff --git a/pkg/validator/toon.go b/pkg/validator/toon.go index 9aa1c0ad..0e161072 100644 --- a/pkg/validator/toon.go +++ b/pkg/validator/toon.go @@ -4,10 +4,14 @@ import ( "encoding/json" "errors" "fmt" + "regexp" + "strconv" "github.com/toon-format/toon-go" ) +var toonLineRe = regexp.MustCompile(`line (\d+):`) + type ToonValidator struct{} var _ Validator = ToonValidator{} @@ -15,6 +19,11 @@ var _ Validator = ToonValidator{} func (ToonValidator) ValidateSyntax(b []byte) (bool, error) { _, err := toon.Decode(b) if err != nil { + if m := toonLineRe.FindStringSubmatch(err.Error()); m != nil { + if line, convErr := strconv.Atoi(m[1]); convErr == nil { + return false, &ValidationError{Err: err, Line: line} + } + } return false, err } return true, nil diff --git a/pkg/validator/validator.go b/pkg/validator/validator.go index a08d1e6b..d0e1e469 100644 --- a/pkg/validator/validator.go +++ b/pkg/validator/validator.go @@ -6,6 +6,17 @@ import "errors" // supports schema validation but does not declare a schema. var ErrNoSchema = errors.New("no schema declared") +// ValidationError wraps a validation error with optional source position. +// Line and Column are 1-based. A zero value means the position is unknown. +type ValidationError struct { + Err error + Line int + Column int +} + +func (e *ValidationError) Error() string { return e.Err.Error() } +func (e *ValidationError) Unwrap() error { return e.Err } + // Validator is the base interface that all validators must implement. // For optional capabilities, use type assertions against SchemaValidator. type Validator interface { diff --git a/pkg/validator/validator_test.go b/pkg/validator/validator_test.go index 9165b3b8..ca8b71a4 100644 --- a/pkg/validator/validator_test.go +++ b/pkg/validator/validator_test.go @@ -2,7 +2,6 @@ package validator import ( _ "embed" - "encoding/json" "errors" "os" "path/filepath" @@ -263,22 +262,12 @@ func FuzzSarifValidator(f *testing.F) { f.Fuzz(fuzzFunction(SarifValidator{})) } -func Test_getCustomErrNonSyntaxError(t *testing.T) { +func Test_ValidationError(t *testing.T) { t.Parallel() - // Unmarshal into a typed struct to trigger UnmarshalTypeError instead of SyntaxError - var target struct { - Key int `json:"key"` - } - input := []byte(`{"key": "not_a_number"}`) - err := json.Unmarshal(input, &target) - if err == nil { - t.Fatal("expected an error") - } - customErr := getCustomErr(input, err) - // Should return the original error unchanged since it's not a SyntaxError - if !errors.Is(customErr, err) { - t.Errorf("expected original error, got: %v", customErr) - } + inner := errors.New("something broke") + ve := &ValidationError{Err: inner, Line: 5, Column: 10} + require.Equal(t, "something broke", ve.Error()) + require.ErrorIs(t, ve, inner) } var schemaTestData = []struct { diff --git a/pkg/validator/xml.go b/pkg/validator/xml.go index fd71f68c..bbe91116 100644 --- a/pkg/validator/xml.go +++ b/pkg/validator/xml.go @@ -6,12 +6,16 @@ import ( "fmt" "os" "path/filepath" + "regexp" + "strconv" "strings" "github.com/lestrrat-go/helium" "github.com/lestrrat-go/helium/xsd" ) +var xmlLineColRe = regexp.MustCompile(`at line (\d+), column (\d+)`) + type XMLValidator struct{} var _ Validator = XMLValidator{} @@ -25,6 +29,11 @@ func (XMLValidator) ValidateSyntax(b []byte) (bool, error) { ctx := context.Background() _, err := helium.NewParser().ValidateDTD(true).Parse(ctx, b) if err != nil { + if m := xmlLineColRe.FindStringSubmatch(err.Error()); m != nil { + line, _ := strconv.Atoi(m[1]) + col, _ := strconv.Atoi(m[2]) + return false, &ValidationError{Err: err, Line: line, Column: col} + } return false, err } return true, nil diff --git a/pkg/validator/yaml.go b/pkg/validator/yaml.go index 7a5e71d8..4381e6c1 100644 --- a/pkg/validator/yaml.go +++ b/pkg/validator/yaml.go @@ -4,6 +4,8 @@ import ( "bufio" "bytes" "encoding/json" + "regexp" + "strconv" "strings" "gopkg.in/yaml.v3" @@ -13,10 +15,17 @@ type YAMLValidator struct{} var _ Validator = YAMLValidator{} +var yamlLineRe = regexp.MustCompile(`yaml: line (\d+):`) + func (YAMLValidator) ValidateSyntax(b []byte) (bool, error) { var output any err := yaml.Unmarshal(b, &output) if err != nil { + if m := yamlLineRe.FindStringSubmatch(err.Error()); m != nil { + if line, convErr := strconv.Atoi(m[1]); convErr == nil { + return false, &ValidationError{Err: err, Line: line} + } + } return false, err } return true, nil From 3c6a755d5d926aa3e32b7ad0746ed035c4a1e6c0 Mon Sep 17 00:00:00 2001 From: Clayton Kehoe Date: Thu, 9 Apr 2026 09:45:23 -0500 Subject: [PATCH 2/8] docs: update CHANGELOG --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4547451e..15691996 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - SARIF reporter now includes `region` with `startLine`/`startColumn` for inline PR annotations in GitHub Actions -- `ValidationError` type with optional `Line`/`Column` fields for structured error positions -- GitHub Action section in README and index referencing `Boeing/validate-configs-action@v2.0.0` ## [2.0.0] - 2026-04-08 From b3a4bbf05d8234ebdd2218673c17ca9bb66db367 Mon Sep 17 00:00:00 2001 From: Clayton Kehoe Date: Thu, 9 Apr 2026 10:57:33 -0500 Subject: [PATCH 3/8] fix: improve XSD validation error messages with detailed diagnostics Use helium ErrorCollector to capture individual validation errors instead of the generic 'xsd: validation failed' message. Errors now include the line number and specific schema violation details. --- pkg/validator/xml.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/validator/xml.go b/pkg/validator/xml.go index bbe91116..d9ec61f9 100644 --- a/pkg/validator/xml.go +++ b/pkg/validator/xml.go @@ -68,7 +68,15 @@ func ValidateXSD(b []byte, schemaPath string) (bool, error) { return false, fmt.Errorf("xml parse error: %w", err) } - if err := xsd.NewValidator(schema).Validate(ctx, doc); err != nil { + ec := helium.NewErrorCollector(ctx, helium.ErrorLevelNone) + if err := xsd.NewValidator(schema).ErrorHandler(ec).Validate(ctx, doc); err != nil { + var msgs []string + for _, e := range ec.Errors() { + msgs = append(msgs, e.Error()) + } + if len(msgs) > 0 { + return false, fmt.Errorf("schema validation failed: %s", strings.Join(msgs, "; ")) + } return false, fmt.Errorf("schema validation failed: %w", err) } From 92250d60ab264c56dc59d81296e4df04707eb9be Mon Sep 17 00:00:00 2001 From: Clayton Kehoe Date: Thu, 9 Apr 2026 11:14:02 -0500 Subject: [PATCH 4/8] feat: separate multiple validation errors across all reporters - Standard: each error on its own indented line - JSON: errors as an array instead of a single joined string - SARIF: each error as its own result entry for per-error annotations - JUnit: each error on its own line within the failure message Add SchemaErrors type to carry individual error messages from JSONSchemaValidate, ValidateXSD, and SARIF schema validation. The CLI populates ValidationErrors slice on Report for reporters to consume. --- cmd/validator/testdata/json_schema.txtar | 20 ++--- cmd/validator/testdata/no_schema.txtar | 2 +- cmd/validator/testdata/schema_map.txtar | 2 +- cmd/validator/testdata/schemastore.txtar | 22 ++--- cmd/validator/testdata/toml_schema.txtar | 2 +- cmd/validator/testdata/toon_schema.txtar | 2 +- cmd/validator/testdata/xml_schema.txtar | 6 +- cmd/validator/testdata/yaml_schema.txtar | 2 +- pkg/cli/cli.go | 23 +++-- pkg/reporter/json_reporter.go | 12 +-- pkg/reporter/junit_reporter.go | 7 +- pkg/reporter/reporter.go | 15 ++-- pkg/reporter/reporter_test.go | 104 +++++++++-------------- pkg/reporter/sarif_reporter.go | 59 ++++++++----- pkg/reporter/stdout_reporter.go | 6 +- pkg/validator/sarif.go | 16 ++++ pkg/validator/schema.go | 3 +- pkg/validator/validator.go | 21 ++++- pkg/validator/xml.go | 2 +- 19 files changed, 183 insertions(+), 143 deletions(-) diff --git a/cmd/validator/testdata/json_schema.txtar b/cmd/validator/testdata/json_schema.txtar index 6a7792ff..ae23161c 100644 --- a/cmd/validator/testdata/json_schema.txtar +++ b/cmd/validator/testdata/json_schema.txtar @@ -11,7 +11,7 @@ stdout '✓' # Invalid package.json (version must be string) ! exec validator invalid_package.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # --- tsconfig.json schema --- @@ -22,7 +22,7 @@ stdout '✓' # Invalid tsconfig.json (target must be string) ! exec validator invalid_tsconfig.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # --- eslintrc schema --- @@ -39,7 +39,7 @@ stdout '✓' # Invalid workflow (missing required jobs) ! exec validator invalid_workflow.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # --- Strict config schema --- @@ -50,27 +50,27 @@ stdout '✓' # Missing required field ! exec validator missing_required.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # Wrong type ! exec validator wrong_type.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # Extra property not allowed ! exec validator extra_property.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # Value out of range ! exec validator out_of_range.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # Enum violation ! exec validator bad_enum.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # --- Array schema --- @@ -81,12 +81,12 @@ stdout '✓' # Too few items ! exec validator too_few_items.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # Wrong item type ! exec validator wrong_item_type.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # --- Mixed: files with and without $schema --- diff --git a/cmd/validator/testdata/no_schema.txtar b/cmd/validator/testdata/no_schema.txtar index 55319989..c69c0de0 100644 --- a/cmd/validator/testdata/no_schema.txtar +++ b/cmd/validator/testdata/no_schema.txtar @@ -6,7 +6,7 @@ stdout '✓' # Without --no-schema it fails ! exec validator bad_with_schema.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # --no-schema with YAML schema comment — skips validation exec validator --no-schema bad_with_schema.yaml diff --git a/cmd/validator/testdata/schema_map.txtar b/cmd/validator/testdata/schema_map.txtar index 20b13b02..7c808a73 100644 --- a/cmd/validator/testdata/schema_map.txtar +++ b/cmd/validator/testdata/schema_map.txtar @@ -5,7 +5,7 @@ stdout '✓' # --schema-map with invalid data fails ! exec validator --schema-map=bad.json:schema.json bad.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # --schema-map with glob pattern exec validator --schema-map=**/*.json:schema.json subdir/config.json diff --git a/cmd/validator/testdata/schemastore.txtar b/cmd/validator/testdata/schemastore.txtar index a5f39d01..0619a0ef 100644 --- a/cmd/validator/testdata/schemastore.txtar +++ b/cmd/validator/testdata/schemastore.txtar @@ -14,7 +14,7 @@ stdout '✓' # Invalid package.json (version must be string) ! exec validator --schemastore schemastore invalid/package.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # Valid nodemon.json exec validator --schemastore schemastore valid/nodemon.json @@ -23,7 +23,7 @@ stdout '✓' # Invalid nodemon.json (unknown field, additionalProperties: false) ! exec validator --schemastore schemastore invalid/nodemon.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # ============================================================ # PART 2: --schemastore automatic matching (YAML) @@ -36,7 +36,7 @@ stdout '✓' # Invalid GitHub workflow (missing required 'jobs') ! exec validator --schemastore schemastore invalid/.github/workflows/ci.yml stdout '×' -stdout 'schema validation failed' +stdout 'error:' # Valid artifacthub-repo.yml exec validator --schemastore schemastore valid/artifacthub-repo.yml @@ -45,7 +45,7 @@ stdout '✓' # Invalid artifacthub-repo.yml (additionalProperties: false) ! exec validator --schemastore schemastore invalid/artifacthub-repo.yml stdout '×' -stdout 'schema validation failed' +stdout 'error:' # ============================================================ # PART 3: --schemastore automatic matching (TOML) @@ -58,7 +58,7 @@ stdout '✓' # Invalid stylua.toml (column_width must be integer) ! exec validator --schemastore schemastore invalid/stylua.toml stdout '×' -stdout 'schema validation failed' +stdout 'error:' # ============================================================ # PART 4: --schemastore automatic matching (TOON) @@ -71,7 +71,7 @@ stdout '✓' # Invalid app.toon (port out of range) ! exec validator --schemastore schemastore invalid/app.toon stdout '×' -stdout 'schema validation failed' +stdout 'error:' # ============================================================ # PART 5: Unmatched files pass syntax-only @@ -115,7 +115,7 @@ stdout '✓' # schema-map points to strict schema that rejects the file ! exec validator --schema-map=package.json:strict_schema.json --schemastore schemastore valid/package.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # ============================================================ # PART 9: --schema-map with YAML @@ -126,7 +126,7 @@ stdout '✓' ! exec validator --schema-map=config.yaml:server_schema.json invalid/config.yaml stdout '×' -stdout 'schema validation failed' +stdout 'error:' # ============================================================ # PART 10: --schema-map with TOML @@ -137,7 +137,7 @@ stdout '✓' ! exec validator --schema-map=config.toml:server_schema.json invalid/config.toml stdout '×' -stdout 'schema validation failed' +stdout 'error:' # ============================================================ # PART 11: --schema-map with TOON @@ -148,7 +148,7 @@ stdout '✓' ! exec validator --schema-map=config.toon:server_schema.json invalid/config.toon stdout '×' -stdout 'schema validation failed' +stdout 'error:' # ============================================================ # PART 12: --schema-map with glob patterns @@ -159,7 +159,7 @@ stdout '✓' ! exec validator --schema-map=**/configs/*.json:server_schema.json invalid/configs/db.json stdout '×' -stdout 'schema validation failed' +stdout 'error:' # ============================================================ # PART 13: Multiple files, mixed results diff --git a/cmd/validator/testdata/toml_schema.txtar b/cmd/validator/testdata/toml_schema.txtar index da149dc0..bf973987 100644 --- a/cmd/validator/testdata/toml_schema.txtar +++ b/cmd/validator/testdata/toml_schema.txtar @@ -5,7 +5,7 @@ stdout '✓' # TOML with invalid data fails schema validation ! exec validator invalid.toml stdout '×' -stdout 'schema validation failed' +stdout 'error:' # TOML without $schema passes (syntax only) exec validator plain.toml diff --git a/cmd/validator/testdata/toon_schema.txtar b/cmd/validator/testdata/toon_schema.txtar index 36c60eaa..d7f9c573 100644 --- a/cmd/validator/testdata/toon_schema.txtar +++ b/cmd/validator/testdata/toon_schema.txtar @@ -5,7 +5,7 @@ stdout '✓' # TOON with invalid data fails schema validation ! exec validator invalid.toon stdout '×' -stdout 'schema validation failed' +stdout 'error:' # TOON without $schema passes (syntax only) exec validator plain.toon diff --git a/cmd/validator/testdata/xml_schema.txtar b/cmd/validator/testdata/xml_schema.txtar index f4688356..2e8b5728 100644 --- a/cmd/validator/testdata/xml_schema.txtar +++ b/cmd/validator/testdata/xml_schema.txtar @@ -61,7 +61,7 @@ stdout '✓' # Invalid type ! exec validator xsd_invalid.xml stdout '×' -stdout 'schema validation failed' +stdout 'error:' # No schema passes syntax-only exec validator plain.xml @@ -87,7 +87,7 @@ stdout '✓' # --schema-map with XSD invalid ! exec validator --schema-map=mapped_bad.xml:schema.xsd mapped_bad.xml stdout '×' -stdout 'schema validation failed' +stdout 'error:' # ============================================================ # DTD + XSD together @@ -100,7 +100,7 @@ stdout '✓' # DTD valid but XSD fails (wrong type) ! exec validator dtd_ok_xsd_bad.xml stdout '×' -stdout 'schema validation failed' +stdout 'error:' # DTD fails (missing element) — never reaches XSD ! exec validator dtd_bad_xsd_ok.xml diff --git a/cmd/validator/testdata/yaml_schema.txtar b/cmd/validator/testdata/yaml_schema.txtar index 02f4ce04..84c44fc8 100644 --- a/cmd/validator/testdata/yaml_schema.txtar +++ b/cmd/validator/testdata/yaml_schema.txtar @@ -5,7 +5,7 @@ stdout '✓' # YAML with invalid data fails schema validation ! exec validator invalid.yaml stdout '×' -stdout 'schema validation failed' +stdout 'error:' # YAML without schema comment passes (syntax only) exec validator plain.yaml diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go index 89ba8819..2f0c93de 100644 --- a/pkg/cli/cli.go +++ b/pkg/cli/cli.go @@ -130,14 +130,23 @@ func (c CLI) Run() (int, error) { col = ve.Column } + var validationErrors []string + var se *validator.SchemaErrors + if errors.As(err, &se) { + validationErrors = se.Errors() + } else if err != nil { + validationErrors = []string{err.Error()} + } + report := reporter.Report{ - FileName: fileToValidate.Name, - FilePath: fileToValidate.Path, - IsValid: isValid, - ValidationError: err, - IsQuiet: Quiet, - StartLine: line, - StartColumn: col, + FileName: fileToValidate.Name, + FilePath: fileToValidate.Path, + IsValid: isValid, + ValidationError: err, + ValidationErrors: validationErrors, + IsQuiet: Quiet, + StartLine: line, + StartColumn: col, } if !isValid { errorFound = true diff --git a/pkg/reporter/json_reporter.go b/pkg/reporter/json_reporter.go index 66338193..636b90a4 100644 --- a/pkg/reporter/json_reporter.go +++ b/pkg/reporter/json_reporter.go @@ -17,9 +17,9 @@ func NewJSONReporter(outputDest string) *JSONReporter { } type fileStatus struct { - Path string `json:"path"` - Status string `json:"status"` - Error string `json:"error,omitempty"` + Path string `json:"path"` + Status string `json:"status"` + Errors []string `json:"errors,omitempty"` } type summary struct { @@ -202,10 +202,10 @@ func createJSONReport(reports []Report) (reportJSON, error) { for _, report := range reports { status := "passed" - errorStr := "" + var errs []string if !report.IsValid { status = "failed" - errorStr = report.ValidationError.Error() + errs = report.ValidationErrors } // Convert Windows-style file paths. @@ -216,7 +216,7 @@ func createJSONReport(reports []Report) (reportJSON, error) { jsonReport.Files = append(jsonReport.Files, fileStatus{ Path: report.FilePath, Status: status, - Error: errorStr, + Errors: errs, }) currentPassed := 0 diff --git a/pkg/reporter/junit_reporter.go b/pkg/reporter/junit_reporter.go index 3116e30f..82a1e2e3 100644 --- a/pkg/reporter/junit_reporter.go +++ b/pkg/reporter/junit_reporter.go @@ -191,8 +191,11 @@ func (jr JunitReporter) Print(reports []Report) error { tc := Testcase{Name: fmt.Sprintf("%s validation", r.FilePath), File: r.FilePath, ClassName: "config-file-validator"} if !r.IsValid { testErrors++ - escapedErrorString := escapeString(r.ValidationError.Error()) - tc.TestcaseFailure = &TestcaseFailure{Message: Message{InnerXML: escapedErrorString}} + var escapedErrors []string + for _, e := range r.ValidationErrors { + escapedErrors = append(escapedErrors, escapeString(e)) + } + tc.TestcaseFailure = &TestcaseFailure{Message: Message{InnerXML: strings.Join(escapedErrors, "\n")}} } testcases = append(testcases, tc) } diff --git a/pkg/reporter/reporter.go b/pkg/reporter/reporter.go index 17266f04..7ecc7a75 100644 --- a/pkg/reporter/reporter.go +++ b/pkg/reporter/reporter.go @@ -3,13 +3,14 @@ package reporter // The Report object stores information about the report // and the results of the validation type Report struct { - FileName string - FilePath string - IsValid bool - ValidationError error - IsQuiet bool - StartLine int - StartColumn int + FileName string + FilePath string + IsValid bool + ValidationError error + ValidationErrors []string + IsQuiet bool + StartLine int + StartColumn int } // Reporter is the interface that wraps the Print method diff --git a/pkg/reporter/reporter_test.go b/pkg/reporter/reporter_test.go index aaf8f671..44fa2b4f 100644 --- a/pkg/reporter/reporter_test.go +++ b/pkg/reporter/reporter_test.go @@ -14,53 +14,38 @@ import ( // Shared test fixtures var ( validReport = Report{ - "good.xml", - "/fake/path/good.xml", - true, - nil, - false, - 0, - 0, + FileName: "good.xml", + FilePath: "/fake/path/good.xml", + IsValid: true, } backslashReport = Report{ - "good.xml", - "\\fake\\path\\good.xml", - true, - nil, - false, - 0, - 0, + FileName: "good.xml", + FilePath: "\\fake\\path\\good.xml", + IsValid: true, } invalidReport = Report{ - "bad.xml", - "/fake/path/bad.xml", - false, - errors.New("unable to parse bad.xml file"), - false, - 0, - 0, + FileName: "bad.xml", + FilePath: "/fake/path/bad.xml", + IsValid: false, + ValidationError: errors.New("unable to parse bad.xml file"), + ValidationErrors: []string{"unable to parse bad.xml file"}, } multiLineErrorReport = Report{ - "bad.xml", - "/fake/path/bad.xml", - false, - errors.New("unable to parse keys:\nkey1\nkey2"), - false, - 0, - 0, + FileName: "bad.xml", + FilePath: "/fake/path/bad.xml", + IsValid: false, + ValidationError: errors.New("unable to parse keys:\nkey1\nkey2"), + ValidationErrors: []string{"unable to parse keys:\nkey1\nkey2"}, } quietReport = Report{ - "good.xml", - "/fake/path/good.xml", - true, - nil, - true, - 0, - 0, + FileName: "good.xml", + FilePath: "/fake/path/good.xml", + IsValid: true, + IsQuiet: true, } mixedReports = []Report{validReport, invalidReport, multiLineErrorReport} @@ -103,13 +88,11 @@ func Test_jsonReportToFile(t *testing.T) { func Test_junitReport(t *testing.T) { reports := []Report{validReport, backslashReport, { - "bad.xml", - "/fake/path/bad.json", - false, - errors.New("Incorrect characters '<' and '` found in file"), - false, - 0, - 0, + FileName: "bad.xml", + FilePath: "/fake/path/bad.json", + IsValid: false, + ValidationError: errors.New("Incorrect characters '<' and '` found in file"), + ValidationErrors: []string{"Incorrect characters '<' and '` found in file"}, }} err := (JunitReporter{}).Print(reports) require.NoError(t, err) @@ -149,22 +132,21 @@ func Test_sarifReport(t *testing.T) { func Test_sarifReportWithRegion(t *testing.T) { reportWithPos := Report{ - "bad.json", - "/fake/path/bad.json", - false, - errors.New("error at line 3 column 10"), - false, - 3, - 10, + FileName: "bad.json", + FilePath: "/fake/path/bad.json", + IsValid: false, + ValidationError: errors.New("error at line 3 column 10"), + ValidationErrors: []string{"error at line 3 column 10"}, + StartLine: 3, + StartColumn: 10, } reportLineOnly := Report{ - "bad.yaml", - "/fake/path/bad.yaml", - false, - errors.New("yaml: line 5: mapping error"), - false, - 5, - 0, + FileName: "bad.yaml", + FilePath: "/fake/path/bad.yaml", + IsValid: false, + ValidationError: errors.New("yaml: line 5: mapping error"), + ValidationErrors: []string{"yaml: line 5: mapping error"}, + StartLine: 5, } var buf bytes.Buffer @@ -247,13 +229,9 @@ func Test_jsonGroupedReports(t *testing.T) { func Test_reporterFileOutput(t *testing.T) { report := Report{ - "good.json", - "/fake/path/good.json", - true, - nil, - false, - 0, - 0, + FileName: "good.json", + FilePath: "/fake/path/good.json", + IsValid: true, } for _, tc := range []struct { diff --git a/pkg/reporter/sarif_reporter.go b/pkg/reporter/sarif_reporter.go index c4b6cc9d..f293012e 100644 --- a/pkg/reporter/sarif_reporter.go +++ b/pkg/reporter/sarif_reporter.go @@ -75,8 +75,6 @@ func NewSARIFReporter(outputDest string) *SARIFReporter { func createSARIFReport(reports []Report) (*SARIFLog, error) { var log SARIFLog - n := len(reports) - log.Version = SARIFVersion log.Schema = SARIFSchema @@ -87,37 +85,52 @@ func createSARIFReport(reports []Report) (*SARIFLog, error) { runs.Tool.Driver.InfoURI = DriverInfoURI runs.Tool.Driver.Version = DriverVersion - runs.Results = make([]result, n) - - for i, report := range reports { + for _, report := range reports { if strings.Contains(report.FilePath, "\\") { report.FilePath = strings.ReplaceAll(report.FilePath, "\\", "/") } - result := &runs.Results[i] - if !report.IsValid { - result.Kind = "fail" - result.Level = "error" - result.Message.Text = report.ValidationError.Error() - } else { - result.Kind = "pass" - result.Level = "none" - result.Message.Text = "No errors detected" + uri := "file:///" + report.FilePath + + if report.IsValid { + runs.Results = append(runs.Results, result{ + Kind: "pass", + Level: "none", + Message: message{Text: "No errors detected"}, + Locations: []location{{ + PhysicalLocation: physicalLocation{ + ArtifactLocation: artifactLocation{URI: uri}, + }, + }}, + }) + continue } - result.Locations = make([]location, 1) - location := &result.Locations[0] - - location.PhysicalLocation.ArtifactLocation.URI = "file:///" + report.FilePath - - if report.StartLine > 0 { - location.PhysicalLocation.Region = ®ion{ - StartLine: report.StartLine, - StartColumn: report.StartColumn, + for _, errMsg := range report.ValidationErrors { + r := result{ + Kind: "fail", + Level: "error", + Message: message{Text: errMsg}, + Locations: []location{{ + PhysicalLocation: physicalLocation{ + ArtifactLocation: artifactLocation{URI: uri}, + }, + }}, } + if report.StartLine > 0 { + r.Locations[0].PhysicalLocation.Region = ®ion{ + StartLine: report.StartLine, + StartColumn: report.StartColumn, + } + } + runs.Results = append(runs.Results, r) } } + if runs.Results == nil { + runs.Results = []result{} + } + return &log, nil } diff --git a/pkg/reporter/stdout_reporter.go b/pkg/reporter/stdout_reporter.go index fbcaea0f..8e5b7e08 100644 --- a/pkg/reporter/stdout_reporter.go +++ b/pkg/reporter/stdout_reporter.go @@ -128,9 +128,11 @@ func createStdoutReport(reports []Report, indentSize int) reportStdout { for _, report := range reports { if !report.IsValid { fmtRed := color.New(color.FgRed) - paddedString := padErrorString(report.ValidationError.Error()) result.Text += fmtRed.Sprintf("%s× %s\n", indent, report.FilePath) - result.Text += fmtRed.Sprintf("%serror: %v\n", errIndent, paddedString) + for _, e := range report.ValidationErrors { + paddedString := padErrorString(e) + result.Text += fmtRed.Sprintf("%serror: %v\n", errIndent, paddedString) + } result.Summary.Failed++ } else { result.Text += color.New(color.FgGreen).Sprintf("%s✓ %s\n", indent, report.FilePath) diff --git a/pkg/validator/sarif.go b/pkg/validator/sarif.go index 96ab3557..5d9cb631 100644 --- a/pkg/validator/sarif.go +++ b/pkg/validator/sarif.go @@ -3,6 +3,7 @@ package validator import ( "bytes" "errors" + "strings" v210 "github.com/owenrumney/go-sarif/v3/pkg/report/v210/sarif" v22 "github.com/owenrumney/go-sarif/v3/pkg/report/v22/sarif" @@ -29,6 +30,21 @@ func (SarifValidator) ValidateSchema(b []byte, _ string) (bool, error) { return false, err } if err := report.Validate(); err != nil { + msg := err.Error() + const prefix = "validation failed: [" + if strings.HasPrefix(msg, prefix) { + inner := strings.TrimPrefix(msg, prefix) + inner = strings.TrimSuffix(inner, "\n]") + var items []string + for _, part := range strings.Split(inner, "\n") { + if s := strings.TrimSpace(part); s != "" { + items = append(items, s) + } + } + if len(items) > 0 { + return false, &SchemaErrors{Prefix: "schema validation failed: ", Items: items} + } + } return false, err } return true, nil diff --git a/pkg/validator/schema.go b/pkg/validator/schema.go index ca8e865d..f1551972 100644 --- a/pkg/validator/schema.go +++ b/pkg/validator/schema.go @@ -4,7 +4,6 @@ import ( "fmt" "net/url" "path/filepath" - "strings" "github.com/xeipuuv/gojsonschema" ) @@ -23,7 +22,7 @@ func JSONSchemaValidate(schemaURL string, docJSON []byte) (bool, error) { for _, desc := range result.Errors() { errs = append(errs, desc.String()) } - return false, fmt.Errorf("schema validation failed: %s", strings.Join(errs, "; ")) + return false, &SchemaErrors{Prefix: "schema validation failed: ", Items: errs} } return true, nil diff --git a/pkg/validator/validator.go b/pkg/validator/validator.go index d0e1e469..766ddfab 100644 --- a/pkg/validator/validator.go +++ b/pkg/validator/validator.go @@ -1,6 +1,9 @@ package validator -import "errors" +import ( + "errors" + "strings" +) // ErrNoSchema is returned by SchemaValidator.ValidateSchema when the document // supports schema validation but does not declare a schema. @@ -17,6 +20,22 @@ type ValidationError struct { func (e *ValidationError) Error() string { return e.Err.Error() } func (e *ValidationError) Unwrap() error { return e.Err } +// SchemaErrors holds multiple schema validation errors. +// The joined Error() string is used for backward compatibility, +// while Errors() returns individual error messages. +type SchemaErrors struct { + Prefix string + Items []string +} + +func (e *SchemaErrors) Error() string { + return e.Prefix + strings.Join(e.Items, "; ") +} + +func (e *SchemaErrors) Errors() []string { + return e.Items +} + // Validator is the base interface that all validators must implement. // For optional capabilities, use type assertions against SchemaValidator. type Validator interface { diff --git a/pkg/validator/xml.go b/pkg/validator/xml.go index d9ec61f9..ffdfb1bf 100644 --- a/pkg/validator/xml.go +++ b/pkg/validator/xml.go @@ -75,7 +75,7 @@ func ValidateXSD(b []byte, schemaPath string) (bool, error) { msgs = append(msgs, e.Error()) } if len(msgs) > 0 { - return false, fmt.Errorf("schema validation failed: %s", strings.Join(msgs, "; ")) + return false, &SchemaErrors{Prefix: "schema validation failed: ", Items: msgs} } return false, fmt.Errorf("schema validation failed: %w", err) } From 136758bbe59917d4c299606306fabfa08e23a6ff Mon Sep 17 00:00:00 2001 From: Clayton Kehoe Date: Thu, 9 Apr 2026 11:33:41 -0500 Subject: [PATCH 5/8] fix: clean up XSD validation error format Reformat helium XSD errors from (string):5: Schemas validity error : Element 'port': ... to line 5: Element 'port': ... Consistent with the error format used by other validators. --- pkg/validator/xml.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pkg/validator/xml.go b/pkg/validator/xml.go index ffdfb1bf..64118d99 100644 --- a/pkg/validator/xml.go +++ b/pkg/validator/xml.go @@ -14,7 +14,10 @@ import ( "github.com/lestrrat-go/helium/xsd" ) -var xmlLineColRe = regexp.MustCompile(`at line (\d+), column (\d+)`) +var ( + xmlLineColRe = regexp.MustCompile(`at line (\d+), column (\d+)`) + xsdErrorLineRe = regexp.MustCompile(`^\(string\):(\d+): Schemas validity error : (.+)`) +) type XMLValidator struct{} @@ -72,7 +75,7 @@ func ValidateXSD(b []byte, schemaPath string) (bool, error) { if err := xsd.NewValidator(schema).ErrorHandler(ec).Validate(ctx, doc); err != nil { var msgs []string for _, e := range ec.Errors() { - msgs = append(msgs, e.Error()) + msgs = append(msgs, cleanXSDError(e.Error())) } if len(msgs) > 0 { return false, &SchemaErrors{Prefix: "schema validation failed: ", Items: msgs} @@ -122,3 +125,14 @@ func resolveXSDPath(schemaLoc, filePath string) string { dir := filepath.Dir(filePath) return filepath.Join(dir, schemaLoc) } + +// cleanXSDError reformats helium XSD errors from +// "(string):5: Schemas validity error : Element 'port': ...\n" +// to "line 5: Element 'port': ..." +func cleanXSDError(s string) string { + s = strings.TrimSpace(s) + if m := xsdErrorLineRe.FindStringSubmatch(s); m != nil { + return fmt.Sprintf("line %s: %s", m[1], m[2]) + } + return s +} From 98a993f2557bed221caebcdfba10e15686361278 Mon Sep 17 00:00:00 2001 From: Clayton Kehoe Date: Thu, 9 Apr 2026 11:45:48 -0500 Subject: [PATCH 6/8] feat: add syntax/schema error prefixes and error-type groupby - Prefix each validation error with 'syntax:' or 'schema:' to distinguish error types across all reporters - Add ErrorType field to Report struct - Add 'error-type' as a new groupby option that groups results into syntax, schema, and Passed categories - Update flag help text, README, and index --- CHANGELOG.md | 2 +- PKGBUILD | 6 +++--- README.md | 4 ++-- cmd/validator/testdata/groupby.txtar | 13 +++++++++++++ cmd/validator/validator.go | 6 +++--- index.md | 4 ++-- pkg/cli/cli.go | 22 ++++++++++++++++++---- pkg/cli/group_output.go | 17 +++++++++++++++++ pkg/cli/group_output_test.go | 17 +++++++++++++++++ pkg/reporter/reporter.go | 1 + 10 files changed, 77 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15691996..c4c32e38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [2.0.1] - 2026-04-09 +## [2.1.0] - 2026-04-09 ### Added diff --git a/PKGBUILD b/PKGBUILD index 0a38c7fe..0ebac9e6 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,14 +1,14 @@ # Maintainer: Clayton Kehoe # Contributor : wiz64 pkgname=config-file-validator -pkgver=1.11.0 +pkgver=2.1.0 pkgrel=1 -pkgdesc="A tool to validate the syntax of configuration files" +pkgdesc="A tool to validate the syntax and schema of configuration files" arch=('x86_64') url="https://github.com/Boeing/config-file-validator" license=('Apache 2.0') depends=('glibc') -makedepends=('go>=1.25' 'git' 'sed') +makedepends=('go>=1.26.2' 'git' 'sed') source=("git+https://github.com/Boeing/config-file-validator.git") sha256sums=('SKIP') md5sums=('SKIP') diff --git a/README.md b/README.md index 7f6c191a..6d99d106 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ optional flags: -globbing If globbing flag is set, check for glob patterns in the arguments. -groupby string - Group output by filetype, directory, pass-fail. Supported for Standard and JSON reports + Group output by filetype, directory, pass-fail, error-type. Supported for Standard and JSON reports -no-schema Disable all schema validation. Only syntax is checked. Cannot be used with --require-schema, --schema-map, or --schemastore. @@ -289,7 +289,7 @@ validator --reporter=json:output.json --reporter=standard /path/to/search ### Group report output -Group the report output by file type, directory, or pass-fail. Supports one or more groupings. +Group the report output by file type, directory, pass-fail, or error-type. Supports one or more groupings. ```shell validator -groupby filetype diff --git a/cmd/validator/testdata/groupby.txtar b/cmd/validator/testdata/groupby.txtar index effe8a82..0c5e0fa1 100644 --- a/cmd/validator/testdata/groupby.txtar +++ b/cmd/validator/testdata/groupby.txtar @@ -11,6 +11,15 @@ stdout 'Summary' exec validator -groupby=pass-fail files stdout 'Passed' +# Group by error-type (all valid files → Passed group) +exec validator -groupby=error-type files +stdout 'Passed' + +# Group by error-type with failures +! exec validator -groupby=error-type mixed +stdout 'syntax' +stdout 'Passed' + # Double grouping exec validator -groupby=filetype,directory files stdout 'json' @@ -39,3 +48,7 @@ stdout 'totalPassed' {"key": "value"} -- files/good.yaml -- key: value +-- mixed/good.json -- +{"key": "value"} +-- mixed/bad.json -- +{"key": value} diff --git a/cmd/validator/validator.go b/cmd/validator/validator.go index 2f2fc4db..288a6e89 100644 --- a/cmd/validator/validator.go +++ b/cmd/validator/validator.go @@ -173,7 +173,7 @@ func getFlags(args []string) (validatorConfig, error) { excludeFileTypesPtr = flagSet.String("exclude-file-types", "", "A comma separated list of file types to ignore") fileTypesPtr = flagSet.String("file-types", "", "A comma separated list of file types to validate") versionPtr = flagSet.Bool("version", false, "Version prints the release version of validator") - groupOutputPtr = flagSet.String("groupby", "", "Group output by filetype, directory, pass-fail. Supported for Standard and JSON reports") + groupOutputPtr = flagSet.String("groupby", "", "Group output by filetype, directory, pass-fail, error-type. Supported for Standard and JSON reports") quietPtr = flagSet.Bool("quiet", false, "If quiet flag is set. It doesn't print any output to stdout.") globbingPrt = flagSet.Bool("globbing", false, "If globbing flag is set, check for glob patterns in the arguments.") requireSchemaPtr = flagSet.Bool("require-schema", false, @@ -321,14 +321,14 @@ func validateReporterConf(conf map[string]string, groupBy *string) error { func validateGroupByConf(groupBy *string) error { groupByCleanString := cleanString("groupby") groupByUserInput := strings.Split(groupByCleanString, ",") - groupByAllowedValues := []string{"filetype", "directory", "pass-fail"} + groupByAllowedValues := []string{"filetype", "directory", "pass-fail", "error-type"} seenValues := make(map[string]bool) // Check that the groupby values are valid and not duplicates if groupBy != nil && isFlagSet("groupby") { for _, groupBy := range groupByUserInput { if !slices.Contains(groupByAllowedValues, groupBy) { - return errors.New("wrong parameter value for groupby, only supports filetype, directory, pass-fail") + return errors.New("wrong parameter value for groupby, only supports filetype, directory, pass-fail, error-type") } if _, ok := seenValues[groupBy]; ok { return errors.New("wrong parameter value for groupby, duplicate values are not allowed") diff --git a/index.md b/index.md index 14786ed5..561f79df 100644 --- a/index.md +++ b/index.md @@ -178,7 +178,7 @@ optional flags: -globbing If globbing flag is set, check for glob patterns in the arguments. -groupby string - Group output by filetype, directory, pass-fail. Supported for Standard and JSON reports + Group output by filetype, directory, pass-fail, error-type. Supported for Standard and JSON reports -no-schema Disable all schema validation. Only syntax is checked. Cannot be used with --require-schema, --schema-map, or --schemastore. @@ -305,7 +305,7 @@ validator --reporter=json:output.json --reporter=standard /path/to/search ### Group report output -Group the report output by file type, directory, or pass-fail. Supports one or more groupings. +Group the report output by file type, directory, pass-fail, or error-type. Supports one or more groupings. ```shell validator -groupby filetype diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go index 2f0c93de..5e552199 100644 --- a/pkg/cli/cli.go +++ b/pkg/cli/cli.go @@ -117,10 +117,21 @@ func (c CLI) Run() (int, error) { return 1, fmt.Errorf("unable to read file: %w", err) } - isValid, err := fileToValidate.FileType.Validator.ValidateSyntax(fileContent) + isValid, syntaxErr := fileToValidate.FileType.Validator.ValidateSyntax(fileContent) + var schemaErr error if isValid { - isValid, err = validateSchema(fileToValidate.FileType.Validator, fileContent, fileToValidate.Path) + isValid, schemaErr = validateSchema(fileToValidate.FileType.Validator, fileContent, fileToValidate.Path) + } + + err = syntaxErr + errorType := "" + if syntaxErr != nil { + errorType = "syntax" + } + if schemaErr != nil { + err = schemaErr + errorType = "schema" } var line, col int @@ -133,9 +144,11 @@ func (c CLI) Run() (int, error) { var validationErrors []string var se *validator.SchemaErrors if errors.As(err, &se) { - validationErrors = se.Errors() + for _, e := range se.Errors() { + validationErrors = append(validationErrors, "schema: "+e) + } } else if err != nil { - validationErrors = []string{err.Error()} + validationErrors = []string{"syntax: " + err.Error()} } report := reporter.Report{ @@ -144,6 +157,7 @@ func (c CLI) Run() (int, error) { IsValid: isValid, ValidationError: err, ValidationErrors: validationErrors, + ErrorType: errorType, IsQuiet: Quiet, StartLine: line, StartColumn: col, diff --git a/pkg/cli/group_output.go b/pkg/cli/group_output.go index 241b9760..2ee997d9 100644 --- a/pkg/cli/group_output.go +++ b/pkg/cli/group_output.go @@ -43,6 +43,21 @@ func GroupByPassFail(reports []reporter.Report) map[string][]reporter.Report { return reportByPassOrFail } +// Group Reports by Error Type (syntax, schema, or passed) +func GroupByErrorType(reports []reporter.Report) map[string][]reporter.Report { + reportByErrorType := make(map[string][]reporter.Report) + + for _, report := range reports { + key := report.ErrorType + if key == "" { + key = "Passed" + } + reportByErrorType[key] = append(reportByErrorType[key], report) + } + + return reportByErrorType +} + // Group Reports by Directory func GroupByDirectory(reports []reporter.Report) map[string][]reporter.Report { reportByDirectory := make(map[string][]reporter.Report) @@ -79,6 +94,8 @@ func GroupBySingle(reports []reporter.Report, groupBy string) (map[string][]repo groupReport = GroupByFileType(reports) case "directory": groupReport = GroupByDirectory(reports) + case "error-type": + groupReport = GroupByErrorType(reports) default: return nil, fmt.Errorf("unable to group by %s", groupBy) } diff --git a/pkg/cli/group_output_test.go b/pkg/cli/group_output_test.go index cf02a8c7..afa5b9a5 100644 --- a/pkg/cli/group_output_test.go +++ b/pkg/cli/group_output_test.go @@ -21,6 +21,7 @@ func Test_ValidGroupOutput(t *testing.T) { {"single directory", []string{"directory"}}, {"single filetype", []string{"filetype"}}, {"single pass-fail", []string{"pass-fail"}}, + {"single error-type", []string{"error-type"}}, {"double directory,pass-fail", []string{"directory", "pass-fail"}}, {"double filetype,directory", []string{"filetype", "directory"}}, {"double pass-fail,filetype", []string{"pass-fail", "filetype"}}, @@ -132,6 +133,22 @@ func Test_GroupByFileType(t *testing.T) { require.Len(t, result["json"], 1) } +func Test_GroupByErrorType(t *testing.T) { + t.Parallel() + + reports := []reporter.Report{ + {FileName: "good.json", FilePath: "/path/good.json", IsValid: true}, + {FileName: "bad_syntax.json", FilePath: "/path/bad_syntax.json", IsValid: false, ErrorType: "syntax"}, + {FileName: "bad_schema.json", FilePath: "/path/bad_schema.json", IsValid: false, ErrorType: "schema"}, + {FileName: "bad_schema2.yaml", FilePath: "/path/bad_schema2.yaml", IsValid: false, ErrorType: "schema"}, + } + + result := GroupByErrorType(reports) + require.Len(t, result["Passed"], 1) + require.Len(t, result["syntax"], 1) + require.Len(t, result["schema"], 2) +} + func Test_GroupByPassFail(t *testing.T) { t.Parallel() diff --git a/pkg/reporter/reporter.go b/pkg/reporter/reporter.go index 7ecc7a75..16e69670 100644 --- a/pkg/reporter/reporter.go +++ b/pkg/reporter/reporter.go @@ -8,6 +8,7 @@ type Report struct { IsValid bool ValidationError error ValidationErrors []string + ErrorType string IsQuiet bool StartLine int StartColumn int From 966af7c8b6f99beca2b32963a5562c264a597847 Mon Sep 17 00:00:00 2001 From: Clayton Kehoe Date: Thu, 9 Apr 2026 11:55:26 -0500 Subject: [PATCH 7/8] docs: update CHANGELOG for 2.1.0 --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4c32e38..9476656f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - SARIF reporter now includes `region` with `startLine`/`startColumn` for inline PR annotations in GitHub Actions +- `ValidationError` type with optional `Line`/`Column` fields for structured error positions +- Multiple validation errors are now separated across all reporters: each error on its own line (standard), array of errors (JSON), individual result entries (SARIF), newline-separated in failure message (JUnit) +- `SchemaErrors` type to carry individual schema validation error messages +- Validation errors are prefixed with `syntax:` or `schema:` to distinguish error types +- `error-type` groupby option to group output by syntax errors, schema errors, and passed files +- GitHub Action section in README and index referencing `Boeing/validate-configs-action@v2.0.0` + +### Fixed + +- XSD validation errors now show detailed diagnostics instead of generic "xsd: validation failed" +- XSD error format cleaned up from `(string):5: Schemas validity error : ...` to `line 5: ...` ## [2.0.0] - 2026-04-08 From 46910413249693ca477cb67f0c881969f5337191 Mon Sep 17 00:00:00 2001 From: Clayton Kehoe Date: Thu, 9 Apr 2026 12:34:44 -0500 Subject: [PATCH 8/8] feat!: add /v2 module path suffix for Go major version compatibility Go modules require v2+ to use a /v2 suffix in the module path. Without it, go install @v2.x.x and go get fail. - Update go.mod module path to github.com/Boeing/config-file-validator/v2 - Update all internal imports to use /v2 prefix - Update go install and code examples in README and index - Update pkg.go.dev badge URLs --- PKGBUILD | 2 +- README.md | 18 +++++++++--------- cmd/validator/validator.go | 14 +++++++------- go.mod | 2 +- index.md | 18 +++++++++--------- pkg/cli/cli.go | 8 ++++---- pkg/cli/cli_test.go | 8 ++++---- pkg/cli/group_output.go | 2 +- pkg/cli/group_output_test.go | 6 +++--- pkg/filetype/file_type.go | 4 ++-- pkg/finder/finder.go | 2 +- pkg/finder/finder_test.go | 8 ++++---- pkg/finder/fsfinder.go | 4 ++-- 13 files changed, 48 insertions(+), 48 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 0ebac9e6..7fa4f5f1 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -8,7 +8,7 @@ arch=('x86_64') url="https://github.com/Boeing/config-file-validator" license=('Apache 2.0') depends=('glibc') -makedepends=('go>=1.26.2' 'git' 'sed') +makedepends=('go>=1.26.1' 'git' 'sed') source=("git+https://github.com/Boeing/config-file-validator.git") sha256sums=('SKIP') md5sums=('SKIP') diff --git a/README.md b/README.md index 6d99d106..6dedefd5 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,8 @@ Awesome Go - - Go Reference + + Go Reference @@ -121,7 +121,7 @@ makepkg -si If you have a go environment on your desktop you can use [go install](https://go.dev/doc/go-get-install-deprecation) to install the validator executable. The validator executable will be installed to the directory named by the GOBIN environment variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH environment variable is not set. ```shell -go install github.com/Boeing/config-file-validator/cmd/validator@latest +go install github.com/Boeing/config-file-validator/v2/cmd/validator@latest ``` ## GitHub Action @@ -486,7 +486,7 @@ import ( "os" "log" - "github.com/Boeing/config-file-validator/pkg/cli" + "github.com/Boeing/config-file-validator/v2/pkg/cli" ) func main() { @@ -515,8 +515,8 @@ import ( "os" "log" - "github.com/Boeing/config-file-validator/pkg/cli" - "github.com/Boeing/config-file-validator/pkg/finder" + "github.com/Boeing/config-file-validator/v2/pkg/cli" + "github.com/Boeing/config-file-validator/v2/pkg/finder" ) func main() { @@ -552,8 +552,8 @@ import ( "os" "log" - "github.com/Boeing/config-file-validator/pkg/cli" - "github.com/Boeing/config-file-validator/pkg/reporter" + "github.com/Boeing/config-file-validator/v2/pkg/cli" + "github.com/Boeing/config-file-validator/v2/pkg/reporter" ) func main() { @@ -672,7 +672,7 @@ cfv := cli.Init( Use a local [SchemaStore](https://github.com/SchemaStore/schemastore) clone for automatic schema lookup by filename. ```go -import "github.com/Boeing/config-file-validator/pkg/schemastore" +import "github.com/Boeing/config-file-validator/v2/pkg/schemastore" store, err := schemastore.Open("/path/to/schemastore") if err != nil { diff --git a/cmd/validator/validator.go b/cmd/validator/validator.go index 288a6e89..39483957 100644 --- a/cmd/validator/validator.go +++ b/cmd/validator/validator.go @@ -45,13 +45,13 @@ import ( "github.com/bmatcuk/doublestar/v4" - configfilevalidator "github.com/Boeing/config-file-validator" - "github.com/Boeing/config-file-validator/pkg/cli" - "github.com/Boeing/config-file-validator/pkg/filetype" - "github.com/Boeing/config-file-validator/pkg/finder" - "github.com/Boeing/config-file-validator/pkg/reporter" - "github.com/Boeing/config-file-validator/pkg/schemastore" - "github.com/Boeing/config-file-validator/pkg/tools" + configfilevalidator "github.com/Boeing/config-file-validator/v2" + "github.com/Boeing/config-file-validator/v2/pkg/cli" + "github.com/Boeing/config-file-validator/v2/pkg/filetype" + "github.com/Boeing/config-file-validator/v2/pkg/finder" + "github.com/Boeing/config-file-validator/v2/pkg/reporter" + "github.com/Boeing/config-file-validator/v2/pkg/schemastore" + "github.com/Boeing/config-file-validator/v2/pkg/tools" ) var flagSet *flag.FlagSet diff --git a/go.mod b/go.mod index 0a9de78d..05738a7e 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/Boeing/config-file-validator +module github.com/Boeing/config-file-validator/v2 go 1.26.1 diff --git a/index.md b/index.md index 561f79df..73a278eb 100644 --- a/index.md +++ b/index.md @@ -43,8 +43,8 @@ canonical_url: https://boeing.github.io/config-file-validator/ Awesome Go - - Go Reference + + Go Reference @@ -137,7 +137,7 @@ makepkg -si If you have a go environment on your desktop you can use [go install](https://go.dev/doc/go-get-install-deprecation) to install the validator executable. The validator executable will be installed to the directory named by the GOBIN environment variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH environment variable is not set. ```shell -go install github.com/Boeing/config-file-validator/cmd/validator@v1.8.1 +go install github.com/Boeing/config-file-validator/v2/cmd/validator@latest ``` ## GitHub Action @@ -502,7 +502,7 @@ import ( "os" "log" - "github.com/Boeing/config-file-validator/pkg/cli" + "github.com/Boeing/config-file-validator/v2/pkg/cli" ) func main() { @@ -531,8 +531,8 @@ import ( "os" "log" - "github.com/Boeing/config-file-validator/pkg/cli" - "github.com/Boeing/config-file-validator/pkg/finder" + "github.com/Boeing/config-file-validator/v2/pkg/cli" + "github.com/Boeing/config-file-validator/v2/pkg/finder" ) func main() { @@ -568,8 +568,8 @@ import ( "os" "log" - "github.com/Boeing/config-file-validator/pkg/cli" - "github.com/Boeing/config-file-validator/pkg/reporter" + "github.com/Boeing/config-file-validator/v2/pkg/cli" + "github.com/Boeing/config-file-validator/v2/pkg/reporter" ) func main() { @@ -688,7 +688,7 @@ cfv := cli.Init( Use a local [SchemaStore](https://github.com/SchemaStore/schemastore) clone for automatic schema lookup by filename. ```go -import "github.com/Boeing/config-file-validator/pkg/schemastore" +import "github.com/Boeing/config-file-validator/v2/pkg/schemastore" store, err := schemastore.Open("/path/to/schemastore") if err != nil { diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go index 5e552199..e1f4a07b 100644 --- a/pkg/cli/cli.go +++ b/pkg/cli/cli.go @@ -8,10 +8,10 @@ import ( "github.com/bmatcuk/doublestar/v4" - "github.com/Boeing/config-file-validator/pkg/finder" - "github.com/Boeing/config-file-validator/pkg/reporter" - "github.com/Boeing/config-file-validator/pkg/schemastore" - "github.com/Boeing/config-file-validator/pkg/validator" + "github.com/Boeing/config-file-validator/v2/pkg/finder" + "github.com/Boeing/config-file-validator/v2/pkg/reporter" + "github.com/Boeing/config-file-validator/v2/pkg/schemastore" + "github.com/Boeing/config-file-validator/v2/pkg/validator" ) var ( diff --git a/pkg/cli/cli_test.go b/pkg/cli/cli_test.go index 812b96b3..6ef49d38 100644 --- a/pkg/cli/cli_test.go +++ b/pkg/cli/cli_test.go @@ -6,10 +6,10 @@ import ( "github.com/stretchr/testify/require" - "github.com/Boeing/config-file-validator/internal/testhelper" - "github.com/Boeing/config-file-validator/pkg/finder" - "github.com/Boeing/config-file-validator/pkg/reporter" - "github.com/Boeing/config-file-validator/pkg/schemastore" + "github.com/Boeing/config-file-validator/v2/internal/testhelper" + "github.com/Boeing/config-file-validator/v2/pkg/finder" + "github.com/Boeing/config-file-validator/v2/pkg/reporter" + "github.com/Boeing/config-file-validator/v2/pkg/schemastore" ) func Test_CLI(t *testing.T) { diff --git a/pkg/cli/group_output.go b/pkg/cli/group_output.go index 2ee997d9..c74da072 100644 --- a/pkg/cli/group_output.go +++ b/pkg/cli/group_output.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/Boeing/config-file-validator/pkg/reporter" + "github.com/Boeing/config-file-validator/v2/pkg/reporter" ) // Group Reports by File Type diff --git a/pkg/cli/group_output_test.go b/pkg/cli/group_output_test.go index afa5b9a5..9f79d6c9 100644 --- a/pkg/cli/group_output_test.go +++ b/pkg/cli/group_output_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/Boeing/config-file-validator/internal/testhelper" - "github.com/Boeing/config-file-validator/pkg/finder" - "github.com/Boeing/config-file-validator/pkg/reporter" + "github.com/Boeing/config-file-validator/v2/internal/testhelper" + "github.com/Boeing/config-file-validator/v2/pkg/finder" + "github.com/Boeing/config-file-validator/v2/pkg/reporter" ) func Test_ValidGroupOutput(t *testing.T) { diff --git a/pkg/filetype/file_type.go b/pkg/filetype/file_type.go index 48b86521..bb0c7e6b 100644 --- a/pkg/filetype/file_type.go +++ b/pkg/filetype/file_type.go @@ -1,8 +1,8 @@ package filetype import ( - "github.com/Boeing/config-file-validator/pkg/tools" - "github.com/Boeing/config-file-validator/pkg/validator" + "github.com/Boeing/config-file-validator/v2/pkg/tools" + "github.com/Boeing/config-file-validator/v2/pkg/validator" ) // The FileType object stores information diff --git a/pkg/finder/finder.go b/pkg/finder/finder.go index 38baba49..335df578 100644 --- a/pkg/finder/finder.go +++ b/pkg/finder/finder.go @@ -1,7 +1,7 @@ package finder import ( - "github.com/Boeing/config-file-validator/pkg/filetype" + "github.com/Boeing/config-file-validator/v2/pkg/filetype" ) // The File Metadata object stores the diff --git a/pkg/finder/finder_test.go b/pkg/finder/finder_test.go index ea94b6ef..3c49caf5 100644 --- a/pkg/finder/finder_test.go +++ b/pkg/finder/finder_test.go @@ -7,10 +7,10 @@ import ( "github.com/stretchr/testify/require" - "github.com/Boeing/config-file-validator/internal/testhelper" - "github.com/Boeing/config-file-validator/pkg/filetype" - "github.com/Boeing/config-file-validator/pkg/tools" - "github.com/Boeing/config-file-validator/pkg/validator" + "github.com/Boeing/config-file-validator/v2/internal/testhelper" + "github.com/Boeing/config-file-validator/v2/pkg/filetype" + "github.com/Boeing/config-file-validator/v2/pkg/tools" + "github.com/Boeing/config-file-validator/v2/pkg/validator" ) func Test_fsFinder(t *testing.T) { diff --git a/pkg/finder/fsfinder.go b/pkg/finder/fsfinder.go index 95227800..8bbeb0a5 100644 --- a/pkg/finder/fsfinder.go +++ b/pkg/finder/fsfinder.go @@ -8,8 +8,8 @@ import ( "github.com/bmatcuk/doublestar/v4" - "github.com/Boeing/config-file-validator/pkg/filetype" - "github.com/Boeing/config-file-validator/pkg/tools" + "github.com/Boeing/config-file-validator/v2/pkg/filetype" + "github.com/Boeing/config-file-validator/v2/pkg/tools" ) type TypeOverride struct {