Repository: google/yamlfmt (go)

Issue:
Doublestar excludes doesn't seem to work:
I have this:

```
doublestar: true
exclude:
  - **/templates/**
formatter:
  type: basic
  indent: 2
  include_document_start: true
```

And I am doing this to prevent the yamlfmt from formatting my `templates/` folder that are in my helm charts, since it messes everything up.

However, it keeps complaining that:
`2023/03/24 14:38:02 yaml: line 3: did not find expected alphabetic or numeric character`.

So I put line 3 in quotes `"- **/templates/**"` and now the error goes away.  However, it doesn't respect my excludes and continues to format things in the `templates/` folder.


Pull request description:
downgrade yamlfmt to go 1.18
Since I was only using one simple feature of go 1.20 and nothing unique from go 1.19, it did not really make any sense to require such a new version. Downgrading it will make it easier for pre-commit users or folks installing through `go install`.

Implemented an Errors collection type to replicate the errors.Join feature from go 1.20.

Fixed a small path matching issue for exclusions. Closes #97 

Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index eadb715..69e8820 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -23,7 +23,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        go: [ '1.20' ]
+        go: [ '1.18', '1.19', '1.20' ]
     steps:
       - uses: actions/checkout@v3
 
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index ed117d0..5bd0786 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -11,7 +11,7 @@ jobs:
     if: startsWith(github.ref, 'refs/tags/')
     strategy:
       matrix:
-        version: ['1.20']
+        version: ['1.18']
     env:
       GORELEASER_CURRENT_TAG: ${{ github.ref_name }}
     steps:
diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
index cfb4e6c..a3d0e1c 100644
--- a/.pre-commit-hooks.yaml
+++ b/.pre-commit-hooks.yaml
@@ -1,6 +1,6 @@
 - id: yamlfmt
   name: yamlfmt 
-  description: This hook uses github.com/google/yamlfmt to format yaml files. Requires golang >1.20 to be installed.
+  description: This hook uses github.com/google/yamlfmt to format yaml files. Requires golang >1.18 to be installed.
   entry: yamlfmt
   language: golang
   types: [yaml]
diff --git a/README.md b/README.md
index 8ee4459..281d2a5 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ To download the `yamlfmt` command, you can download the desired binary from rele
 ```
 go install github.com/google/yamlfmt/cmd/yamlfmt@latest
 ```
-This currently requires Go version 1.20 or greater.
+This currently requires Go version 1.18 or greater.
 
 NOTE: Recommended setup if this is your first time installing Go would be in [this DigitalOcean blog post](https://www.digitalocean.com/community/tutorials/how-to-build-and-install-go-programs).
 
diff --git a/cmd/yamlfmt/config.go b/cmd/yamlfmt/config.go
index 6d9b7ca..f2c64e6 100644
--- a/cmd/yamlfmt/config.go
+++ b/cmd/yamlfmt/config.go
@@ -13,6 +13,7 @@ import (
 	"github.com/braydonk/yaml"
 	"github.com/google/yamlfmt"
 	"github.com/google/yamlfmt/command"
+	"github.com/google/yamlfmt/internal/collections"
 	"github.com/mitchellh/mapstructure"
 )
 
@@ -227,7 +228,7 @@ func makeCommandConfigFromData(configData map[string]any) (*command.Config, erro
 
 func parseFormatterConfigFlag(flagValues []string) (map[string]any, error) {
 	formatterValues := map[string]any{}
-	flagErrors := []error{}
+	flagErrors := collections.Errors{}
 
 	// Expected format: fieldname=value
 	for _, configField := range flagValues {
@@ -259,5 +260,5 @@ func parseFormatterConfigFlag(flagValues []string) (map[string]any, error) {
 		formatterValues[kv[0]] = kv[1]
 	}
 
-	return formatterValues, errors.Join(flagErrors...)
+	return formatterValues, flagErrors.Combine()
 }
diff --git a/engine.go b/engine.go
index d7b8043..2a7efb7 100644
--- a/engine.go
+++ b/engine.go
@@ -1,11 +1,11 @@
 package yamlfmt
 
 import (
-	"errors"
 	"fmt"
 	"os"
 
 	"github.com/RageCage64/multilinediff"
+	"github.com/google/yamlfmt/internal/collections"
 )
 
 type Engine interface {
@@ -98,13 +98,13 @@ func (fds FileDiffs) StrOutputQuiet() string {
 }
 
 func (fds FileDiffs) ApplyAll() error {
-	applyErrs := make([]error, len(fds))
+	applyErrs := make(collections.Errors, len(fds))
 	i := 0
 	for _, diff := range fds {
 		applyErrs[i] = diff.Apply()
 		i++
 	}
-	return errors.Join(applyErrs...)
+	return applyErrs.Combine()
 }
 
 func (fds FileDiffs) ChangedCount() int {
diff --git a/go.mod b/go.mod
index e325b54..cb0d7cc 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/google/yamlfmt
 
-go 1.20
+go 1.18
 
 require (
 	github.com/RageCage64/multilinediff v0.2.0
diff --git a/internal/collections/errors.go b/internal/collections/errors.go
new file mode 100644
index 0000000..719079e
--- /dev/null
+++ b/internal/collections/errors.go
@@ -0,0 +1,20 @@
+package collections
+
+import "errors"
+
+type Errors []error
+
+func (errs Errors) Combine() error {
+	errMessage := ""
+
+	for _, err := range errs {
+		if err != nil {
+			errMessage += err.Error() + "\n"
+		}
+	}
+
+	if len(errMessage) == 0 {
+		return nil
+	}
+	return errors.New(errMessage)
+}
diff --git a/path_collector.go b/path_collector.go
index 613017e..2f9fa68 100644
--- a/path_collector.go
+++ b/path_collector.go
@@ -2,6 +2,7 @@ package yamlfmt
 
 import (
 	"io/fs"
+	"log"
 	"os"
 	"path/filepath"
 	"strings"
@@ -115,7 +116,13 @@ func (c *DoublestarCollector) CollectPaths() ([]string, error) {
 		}
 		excluded := false
 		for _, pattern := range c.Exclude {
-			match, err := doublestar.Match(filepath.Clean(pattern), path)
+			absPath, err := filepath.Abs(path)
+			if err != nil {
+				// I wonder how this could ever happen...
+				log.Printf("could not create absolute path for %s: %v", path, err)
+				continue
+			}
+			match, err := doublestar.PathMatch(filepath.Clean(pattern), absPath)
 			if err != nil {
 				return nil, err
 			}


Test patch:
diff --git a/internal/collections/errors_test.go b/internal/collections/errors_test.go
new file mode 100644
index 0000000..6f0988e
--- /dev/null
+++ b/internal/collections/errors_test.go
@@ -0,0 +1,45 @@
+package collections_test
+
+import (
+	"errors"
+	"strings"
+	"testing"
+
+	"github.com/google/yamlfmt/internal/collections"
+)
+
+func TestErrorsCombine(t *testing.T) {
+	errs := collections.Errors{
+		errors.New("a"),
+		nil,
+		errors.New("c"),
+	}
+	err := errs.Combine()
+	if err == nil {
+		t.Fatal("expected combined err not to be nil")
+	}
+	for _, errEl := range errs {
+		if errEl == nil {
+			continue
+		}
+		if !strings.Contains(err.Error(), errEl.Error()) {
+			t.Fatalf("expected combined err to contain %v, got: %v", errEl, err)
+		}
+	}
+}
+
+func TestErrorsCombineEmpty(t *testing.T) {
+	errs := collections.Errors{}
+	err := errs.Combine()
+	if err != nil {
+		t.Fatalf("expected combined err to be nil, got: %v", err)
+	}
+}
+
+func TestErrorsCombineNilElements(t *testing.T) {
+	errs := collections.Errors{nil, nil, nil}
+	err := errs.Combine()
+	if err != nil {
+		t.Fatalf("expected combined err to be nil, got: %v", err)
+	}
+}