-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathmain.go
More file actions
214 lines (185 loc) · 6.23 KB
/
main.go
File metadata and controls
214 lines (185 loc) · 6.23 KB
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package main
import (
"fmt"
"os"
"path/filepath"
"runtime/debug"
"syscall"
"time"
"github.com/ActiveState/cli/internal/analytics"
"github.com/ActiveState/cli/internal/analytics/client/sync"
"github.com/ActiveState/cli/internal/captain"
"github.com/ActiveState/cli/internal/config"
"github.com/ActiveState/cli/internal/constants"
"github.com/ActiveState/cli/internal/errs"
"github.com/ActiveState/cli/internal/events"
"github.com/ActiveState/cli/internal/exeutils"
"github.com/ActiveState/cli/internal/locale"
"github.com/ActiveState/cli/internal/logging"
"github.com/ActiveState/cli/internal/multilog"
"github.com/ActiveState/cli/internal/output"
"github.com/ActiveState/cli/internal/primer"
"github.com/ActiveState/cli/internal/prompt"
"github.com/ActiveState/cli/internal/rollbar"
"github.com/ActiveState/cli/internal/rtutils/ptr"
"github.com/ActiveState/cli/internal/runbits/panics"
"github.com/ActiveState/cli/internal/updater"
"github.com/ActiveState/cli/pkg/cmdlets/errors"
)
type Params struct {
branch string
force bool
version string
}
func newParams() *Params {
return &Params{}
}
func main() {
var exitCode int
var an analytics.Dispatcher
var cfg *config.Instance
// Handle things like panics, exit codes and the closing of globals
defer func() {
if panics.HandlePanics(recover(), debug.Stack()) {
exitCode = 1
}
if err := cfg.Close(); err != nil {
logging.Error("Failed to close config: %w", err)
}
if err := events.WaitForEvents(5*time.Second, rollbar.Wait, an.Wait, logging.Close); err != nil {
logging.Warning("state-remote-installer failed to wait for events: %v", err)
}
os.Exit(exitCode)
}()
// Set up verbose logging
logging.CurrentHandler().SetVerbose(os.Getenv("VERBOSE") != "")
// Set up rollbar reporting
rollbar.SetupRollbar(constants.StateInstallerRollbarToken)
// Allow starting the installer via a double click
captain.DisableMousetrap()
// Set up configuration handler
cfg, err := config.New()
if err != nil {
logging.Error("Could not set up configuration handler: " + errs.JoinMessage(err))
fmt.Fprintln(os.Stderr, err.Error())
exitCode = 1
}
rollbar.SetConfig(cfg)
// Set up output handler
out, err := output.New("plain", &output.Config{
OutWriter: os.Stdout,
ErrWriter: os.Stderr,
Colored: true,
Interactive: false,
})
if err != nil {
logging.Error("Could not set up output handler: " + errs.JoinMessage(err))
fmt.Fprintln(os.Stderr, err.Error())
exitCode = 1
return
}
an = sync.New(cfg, nil, out)
// Set up prompter
prompter := prompt.New(true, an)
params := newParams()
cmd := captain.NewCommand(
"state-installer",
"",
"Installs or updates the State Tool",
primer.New(nil, out, nil, nil, nil, cfg, nil, nil, an),
[]*captain.Flag{ // The naming of these flags is slightly inconsistent due to backwards compatibility requirements
{
Name: "channel",
Description: "Defaults to 'release'. Specify an alternative channel to install from (eg. beta)",
Value: ¶ms.branch,
},
{
Shorthand: "b", // backwards compatibility
Hidden: true,
Value: ¶ms.branch,
},
{
Name: "version",
Shorthand: "v",
Description: "The version of the State Tool to install",
Value: ¶ms.version,
},
{
Name: "force",
Shorthand: "f",
Description: "Force the installation, overwriting any version of the State Tool already installed",
Value: ¶ms.force,
},
},
[]*captain.Argument{},
func(ccmd *captain.Command, args []string) error {
return execute(out, prompter, cfg, an, args, params)
},
)
err = cmd.Execute(os.Args[1:])
if err != nil {
errors.ReportError(err, cmd, an)
if locale.IsInputError(err) {
logging.Error("Installer input error: " + errs.JoinMessage(err))
} else {
multilog.Critical("Installer error: " + errs.JoinMessage(err))
}
exitCode, err = errors.ParseUserFacing(err)
if err != nil {
out.Error(err)
}
return
}
}
func execute(out output.Outputer, prompt prompt.Prompter, cfg *config.Instance, an analytics.Dispatcher, args []string, params *Params) error {
msg := locale.Tr("tos_disclaimer", constants.TermsOfServiceURLLatest)
msg += locale.Tr("tos_disclaimer_prompt", constants.TermsOfServiceURLLatest)
cont, err := prompt.Confirm(locale.Tr("install_remote_title"), msg, ptr.To(true))
if err != nil {
return errs.Wrap(err, "Could not prompt for confirmation")
}
if !cont {
return locale.NewInputError("install_cancel", "Installation cancelled")
}
branch := params.branch
if branch == "" {
branch = constants.ReleaseBranch
}
// Fetch payload
checker := updater.NewDefaultChecker(cfg, an)
checker.InvocationSource = updater.InvocationSourceInstall // Installing from a remote source is only ever encountered via the install flow
checker.VerifyVersion = false
update, err := checker.CheckFor(branch, params.version)
if err != nil {
return errs.Wrap(err, "Could not retrieve install package information")
}
if update == nil {
return errs.New("No update information could be found.")
}
version := update.Version
if params.branch != "" {
version = fmt.Sprintf("%s (%s)", version, branch)
}
out.Fprint(os.Stdout, locale.Tl("remote_install_downloading", "• Downloading State Tool version [NOTICE]{{.V0}}[/RESET]... ", version))
tmpDir, err := update.DownloadAndUnpack()
if err != nil {
out.Print(locale.Tl("remote_install_status_fail", "[ERROR]x Failed[/RESET]"))
return errs.Wrap(err, "Could not download and unpack")
}
out.Print(locale.Tl("remote_install_status_done", "[SUCCESS]✔ Done[/RESET]"))
env := []string{
constants.InstallerNoSubshell + "=true",
}
_, cmd, err := exeutils.ExecuteAndPipeStd(filepath.Join(tmpDir, constants.StateInstallerCmd+exeutils.Extension), args, env)
if err != nil {
if cmd != nil && cmd.ProcessState.Sys().(syscall.WaitStatus).Exited() {
// The issue happened while running the command itself, meaning the responsibility for conveying the error
// is on the command, rather than us.
return errs.Silence(errs.Wrap(err, "Installer failed"))
}
return errs.Wrap(err, "Could not run installer")
}
out.Print(locale.Tl("remote_install_exit_prompt", "Press ENTER to exit."))
fmt.Scanln(ptr.To("")) // Wait for input from user
return nil
}