-
Notifications
You must be signed in to change notification settings - Fork 37
/
main.go
170 lines (145 loc) · 4.24 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
169
170
package main
import (
"flag"
"fmt"
"io"
"os"
"github.com/nilslice/protolock"
)
const info = `Track your .proto files and prevent changes to messages and services which impact API compatibility.
Copyright Steve Manuel <nilslice@gmail.com>
Released under the BSD-3-Clause license.
`
const usage = `
Usage:
protolock <command> [options]
Commands:
-h, --help, help display the usage information for protolock
init initialize a proto.lock file from current tree
status check for breaking changes and report conflicts
commit rewrite proto.lock file with current tree if no conflicts (--force to override)
Options:
--strict [true] enable strict mode and enforce all built-in rules
--debug [false] enable debug mode and output debug messages
--ignore comma-separated list of filepaths to ignore
--force [false] forces commit to rewrite proto.lock file and disregards warnings
--plugins comma-separated list of executable protolock plugin names
--lockdir [.] directory of proto.lock file
--protoroot [.] root of directory tree containing proto files
--uptodate [false] enforce that proto.lock file is up-to-date with proto files
`
var (
options = flag.NewFlagSet("options", flag.ExitOnError)
debug = options.Bool("debug", false, "toggle debug mode for verbose output")
strict = options.Bool("strict", true, "enable strict mode and enforce all built-in rules")
ignore = options.String("ignore", "", "comma-separated list of filepaths to ignore")
force = options.Bool("force", false, "force commit to rewrite proto.lock file and disregard warnings")
plugins = options.String("plugins", "", "comma-separated list of executable protolock plugin names")
lockDir = options.String("lockdir", ".", "directory of proto.lock file")
protoRoot = options.String("protoroot", ".", "root of directory tree containing proto files")
upToDate = options.Bool("uptodate", false, "enforce that proto.lock file is up-to-date with proto files")
)
func main() {
// exit if no command (i.e. help, -h, --help, init, status, or commit)
if len(os.Args) < 2 {
fmt.Print(info + usage)
os.Exit(0)
}
// parse and set options flags
options.Parse(os.Args[2:])
protolock.SetDebug(*debug)
protolock.SetStrict(*strict)
cfg, err := protolock.NewConfig(
*lockDir,
*protoRoot,
*ignore,
*upToDate,
*debug,
)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// switch through known commands
switch os.Args[1] {
case "-h", "--help", "help":
fmt.Print(usage)
case "init":
r, err := protolock.Init(*cfg)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = saveToLockFile(*cfg, r)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
case "commit":
// if force option is false (default), then disallow commit if
// there are any warnings encountered by runing a status check.
if !*force {
status(cfg)
}
r, err := protolock.Commit(*cfg)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = saveToLockFile(*cfg, r)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
case "status":
status(cfg)
default:
os.Exit(0)
}
}
func status(cfg *protolock.Config) {
report, err := protolock.Status(*cfg)
if err == protolock.ErrOutOfDate {
fmt.Println(logPrefix, "error:", err, "run 'protolock commit'")
// only exit if flag provided for backwards compatibility
if cfg.UpToDate {
os.Exit(2)
}
// don't report the error twice
err = nil
}
if err != protolock.ErrWarningsFound && err != nil {
fmt.Println(logPrefix, "error:", err)
os.Exit(1)
}
// if plugins are provided, attempt to execute each as a executable
// located in the user's OS executable path as reported by stdlib's
// exec.LookPath func
if *plugins != "" {
report, err = runPlugins(*plugins, report, *debug)
if err != nil {
fmt.Println(logPrefix, "error:", err)
os.Exit(1)
}
}
code, err := protolock.HandleReport(report, os.Stdout, err)
if err != protolock.ErrWarningsFound && err != nil {
fmt.Println(logPrefix, "error:", err)
os.Exit(1)
}
if code != 0 {
os.Exit(code)
}
}
func saveToLockFile(cfg protolock.Config, r io.Reader) error {
lockfile, err := os.Create(cfg.LockFilePath())
if err != nil {
return err
}
defer lockfile.Close()
_, err = io.Copy(lockfile, r)
if err != nil {
return err
}
return nil
}