-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
168 lines (143 loc) · 3.6 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package main
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"sort"
"strings"
// pflag because --flag is more "normal" than -flag
flag "github.com/spf13/pflag"
"gopkg.in/yaml.v3"
)
func main() {
// arguments/flags
path := flag.String("path", "", "relative or absolute path to read files from")
indent := flag.Int("indent", 2, "output yaml indentation level")
hidden := flag.Bool("hidden", false, "including looking for hidden files")
excluded := flag.String("exclude", "", "files to exclude")
flag.Parse()
// show a human friendly message if --path isn't specified
if !isFlagPassed("path") {
fmt.Println("# error, --path must be provided")
fmt.Printf("\nUsage of yaml-concate:\n")
flag.PrintDefaults()
os.Exit(102)
}
matches, err := findYaml(*path, *excluded, *hidden)
// sort them
sort.Strings(matches)
if err != nil {
fmt.Printf("# error?\n")
os.Exit(101)
}
for _, i := range matches {
// TODO: check formatFile works BEFORE inserting "---" into the stream, avoiding potential for empty docs
fmt.Println("---")
fmt.Fprintf(os.Stderr, "# reading : %s\n", i)
formatFile(i, *indent, false)
}
}
func formatFile(f string, indent int, overwrite bool) {
r, err := os.Open(f)
if err != nil {
log.Fatal(err)
}
var out bytes.Buffer
if e := formatStream(r, &out, indent); e != nil {
log.Fatalf("Failed formatting YAML stream: %v", e)
}
r.Close()
if e := dumpStream(&out, f, overwrite); e != nil {
log.Fatalf("Cannot overwrite: %v", e)
}
}
func formatStream(r io.Reader, out io.Writer, indent int) error {
d := yaml.NewDecoder(r)
in := yaml.Node{}
err := d.Decode(&in)
for err == nil {
enc := yaml.NewEncoder(out)
enc.SetIndent(indent)
if err := enc.Encode(sortYAML(&in)); err != nil {
log.Fatal(err)
}
enc.Close()
if err = d.Decode(&in); err == nil {
fmt.Fprintln(out, "---")
}
}
if err != nil && err != io.EOF {
return err
}
return nil
}
func dumpStream(out *bytes.Buffer, f string, overwrite bool) error {
if overwrite {
return ioutil.WriteFile(f, out.Bytes(), 0744)
}
_, err := io.Copy(os.Stdout, out)
return err
}
func isHidden(path string) bool {
// get just the filename without base path
file := filepath.Base(path)
// return true if its first character is a fullstop
return strings.HasPrefix(file, ".")
}
func findYaml(root, excluded string, includeHidden bool) ([]string, error) {
fmt.Fprintf(os.Stderr, "# searching path: %s\n", root)
// acceptable yaml extension patterns
yamlPatterns := []string{
"*.yaml",
"*.yml",
}
// we'll store a slice of files matching the pattern here
var matches []string
// attempt to walk a folder looking for files that match the extensions
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
// TODO: feels bad :/ please improve me
for _, i := range yamlPatterns {
matched, err := filepath.Match(i, filepath.Base(path))
if err != nil {
// TODO: unsure how to replicate this situation, test me and improve the reporting..
fmt.Printf("\n # bad error :/\n")
os.Exit(100)
}
// the level of if's here is terribad.. please david fix me and write tests :/
if matched {
if includeHidden {
matches = append(matches, path)
} else {
if !isHidden(path) {
matches = append(matches, path)
}
}
}
}
// finished..
return nil
})
if err != nil {
return nil, err
}
return matches, nil
}
func isFlagPassed(name string) bool {
found := false
flag.Visit(func(f *flag.Flag) {
if f.Name == name {
found = true
}
})
return found
}