106 lines
2.5 KiB
Go
106 lines
2.5 KiB
Go
package common
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"strconv"
|
||
|
||
"github.com/spf13/cobra"
|
||
|
||
"github.com/gitlink-org/gitlink-cli/cmd/cmdutil"
|
||
)
|
||
|
||
// MountShortcut converts a Shortcut into a cobra.Command and adds it as a subcommand.
|
||
func MountShortcut(parent *cobra.Command, s *Shortcut) {
|
||
cmd := &cobra.Command{
|
||
Use: "+" + s.Name,
|
||
Short: s.Description,
|
||
RunE: func(cmd *cobra.Command, args []string) error {
|
||
// Collect flag values
|
||
flagValues := make(map[string]string)
|
||
for _, f := range s.Flags {
|
||
if f.Bool {
|
||
val, _ := cmd.Flags().GetBool(f.Name)
|
||
flagValues[f.Name] = strconv.FormatBool(val)
|
||
continue
|
||
}
|
||
val, _ := cmd.Flags().GetString(f.Name)
|
||
if val != "" {
|
||
flagValues[f.Name] = val
|
||
}
|
||
}
|
||
|
||
commandName := parent.Use + " +" + s.Name
|
||
ctx, err := NewRuntimeContext(flagValues, commandName)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// Dry-run interception
|
||
if s.DryRun {
|
||
if dryRunVal, _ := cmd.Flags().GetBool("dry-run"); dryRunVal {
|
||
flagValues["dry-run"] = "true"
|
||
if s.DryRunHint != nil {
|
||
hint, err := s.DryRunHint(ctx)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
fmt.Fprintf(os.Stderr, "[dry-run] %s\n", hint)
|
||
}
|
||
proceed, err := ConfirmAction(ctx)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if !proceed {
|
||
return nil
|
||
}
|
||
}
|
||
}
|
||
|
||
err = s.Run(ctx)
|
||
if err != nil {
|
||
// 当错误为已识别的 API/CLI 错误时,按 envelope 格式输出到 stdout,
|
||
// 让 `--format json` 输出可被 jq 解析的标准结构。
|
||
// 已识别后返回 ErrSilent:保留非零退出码,但 cmd.Execute 不会再 stderr 重复输出。
|
||
if TryPrintError(err, ctx.Format) {
|
||
return cmdutil.ErrSilent
|
||
}
|
||
}
|
||
return err
|
||
},
|
||
}
|
||
|
||
for _, f := range s.Flags {
|
||
usage := f.Usage
|
||
if f.Required {
|
||
usage = usage + " [required]"
|
||
}
|
||
if f.Bool {
|
||
defaultValue, _ := strconv.ParseBool(f.Default)
|
||
if f.Short != "" {
|
||
cmd.Flags().BoolP(f.Name, f.Short, defaultValue, usage)
|
||
} else {
|
||
cmd.Flags().Bool(f.Name, defaultValue, usage)
|
||
}
|
||
} else if f.Short != "" {
|
||
cmd.Flags().StringP(f.Name, f.Short, f.Default, usage)
|
||
} else {
|
||
cmd.Flags().String(f.Name, f.Default, usage)
|
||
}
|
||
}
|
||
|
||
// Auto-register --dry-run flag for shortcuts that support it
|
||
if s.DryRun {
|
||
cmd.Flags().Bool("dry-run", false, "Preview the operation without executing")
|
||
}
|
||
|
||
parent.AddCommand(cmd)
|
||
}
|
||
|
||
// MountShortcuts mounts multiple shortcuts under a parent command.
|
||
func MountShortcuts(parent *cobra.Command, shortcuts []*Shortcut) {
|
||
for _, s := range shortcuts {
|
||
MountShortcut(parent, s)
|
||
}
|
||
}
|