122 lines
2.7 KiB
Go
122 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
const promptCommandTimeout = 5 * time.Second
|
|
|
|
type promptRequest struct {
|
|
title string
|
|
message string
|
|
subtitle string
|
|
}
|
|
|
|
type promptChannel struct {
|
|
name string
|
|
available func() error
|
|
run func(promptRequest) error
|
|
}
|
|
|
|
type commandRunner interface {
|
|
LookPath(file string) (string, error)
|
|
Run(timeout time.Duration, env []string, name string, args ...string) error
|
|
}
|
|
|
|
type defaultCommandRunner struct{}
|
|
|
|
var buildPromptChannels = platformStartupChannels
|
|
|
|
func (defaultCommandRunner) LookPath(file string) (string, error) {
|
|
return exec.LookPath(file)
|
|
}
|
|
|
|
func (defaultCommandRunner) Run(timeout time.Duration, env []string, name string, args ...string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
defer cancel()
|
|
|
|
cmd := exec.CommandContext(ctx, name, args...)
|
|
if len(env) > 0 {
|
|
cmd.Env = append(os.Environ(), env...)
|
|
}
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return err
|
|
}
|
|
if err := ctx.Err(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func showError(title, message string) {
|
|
reportPrompt(promptRequest{title: title, message: message}, os.Stderr, dialogLogger())
|
|
}
|
|
|
|
func reportStartupFailure(err error, logger *zap.Logger) {
|
|
if err == nil {
|
|
return
|
|
}
|
|
|
|
var startupErr *startupError
|
|
if !errors.As(err, &startupErr) {
|
|
startupErr = newStartupError(phaseServer, startupServerErrorMessage(), err)
|
|
}
|
|
|
|
if logger == nil {
|
|
logger = dialogLogger()
|
|
}
|
|
logger.Error("desktop 启动失败",
|
|
zap.String("phase", startupErr.Phase()),
|
|
zap.Error(startupErr))
|
|
|
|
reportPrompt(promptRequest{
|
|
title: startupTitle(),
|
|
message: startupErr.UserMessage(),
|
|
subtitle: startupErr.Phase(),
|
|
}, os.Stderr, logger)
|
|
}
|
|
|
|
func reportPrompt(req promptRequest, fallback io.Writer, logger *zap.Logger) {
|
|
runPromptPipeline(req, buildPromptChannels(defaultCommandRunner{}), fallback, logger)
|
|
}
|
|
|
|
func runPromptPipeline(req promptRequest, channels []promptChannel, fallback io.Writer, logger *zap.Logger) {
|
|
if logger == nil {
|
|
logger = dialogLogger()
|
|
}
|
|
|
|
for _, channel := range channels {
|
|
if channel.available != nil {
|
|
if err := channel.available(); err != nil {
|
|
logger.Warn("提示通道不可用", zap.String("channel", channel.name), zap.Error(err))
|
|
continue
|
|
}
|
|
}
|
|
|
|
if err := channel.run(req); err != nil {
|
|
logger.Warn("提示通道执行失败", zap.String("channel", channel.name), zap.Error(err))
|
|
continue
|
|
}
|
|
return
|
|
}
|
|
|
|
writePromptFallback(fallback, req.title, req.message)
|
|
}
|
|
|
|
func writePromptFallback(w io.Writer, title, message string) {
|
|
if w == nil {
|
|
return
|
|
}
|
|
if _, err := io.WriteString(w, "错误: "+title+": "+message+"\n"); err != nil {
|
|
return
|
|
}
|
|
}
|