forked from fastly/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
152 lines (134 loc) · 4.29 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
// Package main is the entry point for the Fastly CLI.
package main
import (
"errors"
"io"
"net/http"
"os"
"strings"
"time"
"github.com/fatih/color"
"github.com/fastly/cli/pkg/app"
"github.com/fastly/cli/pkg/config"
fsterr "github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/github"
"github.com/fastly/cli/pkg/manifest"
"github.com/fastly/cli/pkg/sync"
)
func main() {
// Some configuration options can come from env vars.
var env config.Environment
env.Read(parseEnv(os.Environ()))
// All of the work of building the set of commands and subcommands, wiring
// them together, picking which one to call, and executing it, occurs in a
// helper function, Run. We parameterize all of the dependencies so we can
// test it more easily. Here, we declare all of the dependencies, using
// the "real" versions that pull e.g. actual commandline arguments, the
// user's real environment, etc.
var (
args = os.Args[1:]
clientFactory = app.FastlyAPIClient
httpClient = &http.Client{Timeout: time.Minute * 2}
in io.Reader = os.Stdin
out io.Writer = sync.NewWriter(color.Output)
)
// We have to manually handle the inclusion of the verbose flag here because
// Kingpin doesn't evaluate the provided arguments until app.Run which
// happens later in the file and yet we need to know if we should be printing
// output related to the application configuration file in this file.
var verboseOutput bool
for _, seg := range args {
if seg == "-v" || seg == "--verbose" {
verboseOutput = true
}
}
// Similarly for the --auto-yes/--non-interactive flags, we need access to
// these for handling interactive error prompts to the user, in case the CLI
// is being run in a CI environment.
var autoYes, nonInteractive bool
for _, seg := range args {
if seg == "-y" || seg == "--auto-yes" {
autoYes = true
}
if seg == "-i" || seg == "--non-interactive" {
nonInteractive = true
}
}
// Extract a subset of configuration options from the local application directory.
var cfg config.File
cfg.SetAutoYes(autoYes)
cfg.SetNonInteractive(nonInteractive)
// The CLI relies on a valid configuration, otherwise we can't continue.
err := cfg.Read(config.FilePath, in, out, fsterr.Log, verboseOutput)
if err != nil {
fsterr.Deduce(err).Print(color.Error)
// WARNING: os.Exit will exit, and any `defer` calls will not be run.
os.Exit(1)
}
var md manifest.Data
md.File.Args = args
md.File.SetErrLog(fsterr.Log)
md.File.SetOutput(out)
// NOTE: We skip handling the error because not all commands relate to C@E.
_ = md.File.Read(manifest.Filename)
// Main is basically just a shim to call Run, so we do that here.
opts := app.RunOpts{
APIClient: clientFactory,
Args: args,
ConfigFile: cfg,
ConfigPath: config.FilePath,
Env: env,
ErrLog: fsterr.Log,
HTTPClient: httpClient,
Manifest: &md,
Stdin: in,
Stdout: out,
Versioners: app.Versioners{
CLI: github.New(github.Opts{
HTTPClient: httpClient,
Org: "fastly",
Repo: "cli",
Binary: "fastly",
}),
Viceroy: github.New(github.Opts{
HTTPClient: httpClient,
Org: "fastly",
Repo: "viceroy",
Binary: "viceroy",
Version: md.File.LocalServer.ViceroyVersion,
}),
},
}
err = app.Run(opts)
// NOTE: We persist any error log entries to disk before attempting to handle
// a possible error response from app.Run as there could be errors recorded
// during the execution flow but were otherwise handled without bubbling an
// error back the call stack, and so if the user still experiences something
// unexpected we will have a record of any errors that happened along the way.
logErr := fsterr.Log.Persist(fsterr.LogPath, args)
if logErr != nil {
fsterr.Deduce(logErr).Print(color.Error)
}
if err != nil {
fsterr.Deduce(err).Print(color.Error)
exitError := fsterr.SkipExitError{}
if errors.As(err, &exitError) {
if exitError.Skip {
return // skip returning an error for 'help' output
}
}
os.Exit(1)
}
}
func parseEnv(environ []string) map[string]string {
env := map[string]string{}
for _, kv := range environ {
toks := strings.SplitN(kv, "=", 2)
if len(toks) != 2 {
continue
}
k, v := toks[0], toks[1]
env[k] = v
}
return env
}