Skip to content

Commit

Permalink
feat: improve error diagnosis of lint issues
Browse files Browse the repository at this point in the history
  • Loading branch information
smlx committed Nov 1, 2021
1 parent ae4b268 commit 956e4dd
Showing 1 changed file with 46 additions and 25 deletions.
71 changes: 46 additions & 25 deletions internal/lagoonyml/lint.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package lagoonyml

import (
"encoding/json"
"errors"
"fmt"
"os"

Expand All @@ -10,19 +12,34 @@ import (
// Linter validates the given Lagoon struct.
type Linter func(*Lagoon) error

// LintFile takes a file path, reads it, and applies `.lagoon.yml` lint policy to
// it. Lint returns an error of type ErrLint if it finds problems with the
// file, a regular error if something else went wrong, and nil if the
// `.lagoon.yml` is valid.
func LintFile(path string, linters ...Linter) error {
func lint(rawYAML []byte, linters []Linter) error {
var l Lagoon
rawYAML, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("couldn't read %v: %v", path, err)
}
err = yaml.Unmarshal(rawYAML, &l)
if err != nil {
return fmt.Errorf("couldn't unmarshal %v: %v", path, err)
yamlErr := yaml.Unmarshal(rawYAML, &l)
if yamlErr != nil {
// try to extract more detail from the Unmarshal error
rawJSON, err := yaml.YAMLToJSON(rawYAML)
if err != nil {
// can't even convert this so return the original error
return yamlErr
}
// json.Unmarshal returns richer errors than yaml.Unmarshal, which helps to
// diagnose exactly what went wrong
err = json.Unmarshal(rawJSON, &l)
var jTypeErr *json.UnmarshalTypeError
if errors.As(err, &jTypeErr) {
// this rawJSON slice will be partial JSON, but JSONToYAML ignores junk
// at the end of the snippet.
badYAML, err := yaml.JSONToYAML(rawJSON[jTypeErr.Offset:])
if err != nil {
// can't convert this snippet, so return the original error
return yamlErr
}
return fmt.Errorf(
"couldn't unmarshal YAML: %v.\nThere appears to be invalid YAML in the `%s` field:\n\n%s",
yamlErr, jTypeErr.Field, badYAML)
}
// this isn't a json.UnmarshalTypeError, so just return the original error
return yamlErr
}
for _, linter := range linters {
if err := linter(&l); err != nil {
Expand All @@ -34,21 +51,25 @@ func LintFile(path string, linters ...Linter) error {
return nil
}

// LintYAML takes a byte slice containing raw YAML and applies `.lagoon.yml` lint policy to
// it. Lint returns an error of type ErrLint if it finds problems with the
// file, a regular error if something else went wrong, and nil if the
// LintFile takes a file path, reads it, and applies `.lagoon.yml` lint policy
// to it. LintFile returns an error of type ErrLint if it finds problems with
// the file, a regular error if something else went wrong, and nil if the
// `.lagoon.yml` is valid.
func LintYAML(rawYAML []byte, linters ...Linter) error {
var l Lagoon
if err := yaml.Unmarshal(rawYAML, &l); err != nil {
return fmt.Errorf("couldn't unmarshal YAML: %v", err)
func LintFile(path string, linters ...Linter) error {
rawYAML, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("couldn't read %v: %v", path, err)
}
for _, linter := range linters {
if err := linter(&l); err != nil {
return &ErrLint{
Detail: err.Error(),
}
}
if err = lint(rawYAML, linters); err != nil {
return fmt.Errorf("couldn't validate: %v: %v", path, err)
}
return nil
}

// LintYAML takes a byte slice containing raw YAML and applies `.lagoon.yml`
// lint policy to it. LintYAML returns an error of type ErrLint if it finds
// problems with the YAML, a regular error if something else went wrong, and
// nil if the YAML is valid.
func LintYAML(rawYAML []byte, linters ...Linter) error {
return lint(rawYAML, linters)
}

0 comments on commit 956e4dd

Please sign in to comment.