From e244c23b5275584e634e0ed473570d78b209acc1 Mon Sep 17 00:00:00 2001 From: Odboy Date: Sun, 1 Nov 2026 08:43:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=B8=AE=E5=8A=A9=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E5=8C=96=E5=B9=B6=E6=8A=8A=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E5=88=86=E7=BB=84=E8=AF=B4=E6=98=8E=E5=86=99=E7=9B=B4=E7=99=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 根命令增加简介与常用示例,分组标题由「disk 相关命令」改为用途说明 (如「磁盘与文件占用:查看哪个目录、哪个文件最占空间」) - cobra 固定文案改为中文:用法 / 可用的子命令 / 选项、[flags] → [选项]、 help for xxx → 显示帮助;completion 与 help 也改为中文说明 - disk usage 摘要改为「看哪个目录或文件最占空间」 --- internal/cli/cli.go | 155 ++++++++++++++++++++++++++++++++--- internal/cli/cli_test.go | 52 ++++++++++++ internal/registry/builtin.go | 2 +- 3 files changed, 197 insertions(+), 12 deletions(-) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 7e868f7..3bd804e 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -59,9 +59,18 @@ func (a *App) Execute(args []string) int { // Root builds the full cobra command tree. func (a *App) Root() *cobra.Command { root := &cobra.Command{ - Use: "kopsc", - Short: "跨平台命令助手:用统一的意图命令在各平台执行对应命令", - Long: "kopsc 用平台无关的意图命令(如 docker clearsafe),根据操作系统、发行版与已安装的运行时,选择并执行对应的底层命令。", + Use: "kopsc", + Short: "跨平台命令助手:用统一的意图命令在各平台执行对应命令", + Long: `kopsc 用平台无关的意图命令(如 docker clearsafe),根据操作系统、发行版与已安装的运行时, +自动选择并执行对应的底层命令。你只需记住要做什么,不必记住每个平台的写法。 + +常用示例: + kopsc docker clearsafe 安全清理未使用的容器、网络与悬空镜像 + kopsc diagnose web 诊断容器或 Pod 为什么起不来(自动识别 Docker / K8s) + kopsc net ports 列出本机正在监听的端口 + kopsc disk usage 看当前目录里谁最占空间 + kopsc proc nohup ./server 后台运行并写日志(等价 nohup ./server > server.out 2>&1 &) + kopsc list 列出全部命令及在本平台实际会执行的命令`, Version: a.Version, SilenceUsage: true, SilenceErrors: true, @@ -72,15 +81,35 @@ func (a *App) Root() *cobra.Command { root.SetOut(a.Stdout) root.SetErr(a.Stderr) root.SetIn(a.Stdin) + localizeCobra(root) for i := range a.Registry.Commands { a.addRegistryCommand(root, &a.Registry.Commands[i]) } root.AddCommand(a.newListCmd(), a.newExplainCmd(), a.newPlatformCmd(), a.newVersionCmd()) + localizeBuiltins(root) + localizeHelpFlags(root) return root } +// localizeBuiltins gives cobra's generated commands Chinese descriptions. +// cobra creates completion and help lazily, so they are initialised here +// explicitly and then renamed. +func localizeBuiltins(root *cobra.Command) { + root.InitDefaultCompletionCmd() + root.InitDefaultHelpCmd() + + if cmd := findChild(root, "completion"); cmd != nil { + cmd.Short = "生成 shell 补全脚本(bash / zsh / fish / powershell)" + cmd.Long = "为 bash、zsh、fish 或 powershell 生成 kopsc 的命令补全脚本。" + } + if cmd := findChild(root, "help"); cmd != nil { + cmd.Short = "查看任意命令的帮助" + cmd.Long = "查看任意命令的帮助,例如 kopsc help disk,效果等同 kopsc disk --help。" + } +} + // addRegistryCommand turns "docker.clearsafe" into `kopsc docker clearsafe`, // reusing intermediate commands for shared ID prefixes. func (a *App) addRegistryCommand(root *cobra.Command, cmd *registry.Command) { @@ -90,14 +119,7 @@ func (a *App) addRegistryCommand(root *cobra.Command, cmd *registry.Command) { for _, group := range parts[:len(parts)-1] { child := findChild(parent, group) if child == nil { - groupName := group - child = &cobra.Command{ - Use: groupName, - Short: groupName + " 相关命令", - RunE: func(c *cobra.Command, args []string) error { - return c.Help() - }, - } + child = newGroup(group) parent.AddCommand(child) } parent = child @@ -107,6 +129,117 @@ func (a *App) addRegistryCommand(root *cobra.Command, cmd *registry.Command) { parent.AddCommand(leaf) } +// localizeCobra replaces cobra's built-in English strings with Chinese ones. +// +// cobra renders the text itself (its templates use cobra's own functions such +// as rpad), so kopsc swaps the templates for Chinese headings and pre-registers +// the help and version flags: cobra only generates its English descriptions +// ("help for kopsc") for flags it has not seen yet. +func localizeCobra(root *cobra.Command) { + cobra.AddTemplateFunc("kopscOptions", func(line string) string { + return strings.ReplaceAll(line, "[flags]", "[选项]") + }) + + root.Use = "kopsc" + root.SetUsageTemplate(usageTemplate) + root.SetHelpTemplate(helpTemplate) + + root.InitDefaultHelpFlag() + root.InitDefaultVersionFlag() + root.Flags().Lookup("help").Usage = "显示帮助" + if flag := root.Flags().Lookup("version"); flag != nil { + flag.Usage = "显示版本" + } + +} + +// localizeHelpFlags pre-registers cobra's help flag on every command with a +// Chinese description. It must run after the command tree is complete, since +// cobra only generates the English default for commands it has not seen. +func localizeHelpFlags(root *cobra.Command) { + queue := []*cobra.Command{root} + for len(queue) > 0 { + c := queue[0] + queue = queue[1:] + c.InitDefaultHelpFlag() + if flag := c.Flags().Lookup("help"); flag != nil { + flag.Usage = "显示帮助" + } + queue = append(queue, c.Commands()...) + } +} + +// usageTemplate is cobra's default usage layout with Chinese headings. +const usageTemplate = `用法:{{if .Runnable}} + {{.UseLine | kopscOptions}}{{end}}{{if .HasAvailableSubCommands}} + {{.CommandPath}} [命令]{{end}}{{if gt (len .Aliases) 0}} + +别名: + {{.NameAndAliases}}{{end}}{{if .HasExample}} + +示例: +{{.Example}}{{end}}{{if .HasAvailableSubCommands}} + +可用的子命令:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} + +选项: +{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} + +全局选项: +{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} + +更多帮助: + {{.CommandPath}} [命令] --help{{end}} +` + +// helpTemplate is cobra's default help layout. +const helpTemplate = `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}} + +{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` + +// helpTextReplacements rewrites the few English fragments cobra still emits. +var helpTextReplacements = []struct{ old, new string }{ + {"[flags]", "[选项]"}, + {"help for ", "查看 "}, + {"Generate the autocompletion script for the specified shell", "生成 shell 补全脚本(bash / zsh / fish / powershell)"}, + {"Help about any command", "查看任意命令的帮助"}, +} + +// translateHelp applies helpTextReplacements. +func translateHelp(text string) string { + for _, r := range helpTextReplacements { + text = strings.ReplaceAll(text, r.old, r.new) + } + return text +} + +// groupDescriptions explains what each command group is for. Without them +// cobra would show "disk 相关命令", which tells a user nothing. +var groupDescriptions = map[string]string{ + "docker": "Docker 容器与镜像管理(查看、启停、日志、清理、导出/导入、Compose)", + "disk": "磁盘与文件占用:查看哪个目录、哪个文件最占空间", + "net": "网络与端口:端口被谁占用、本机监听了哪些端口", + "pkg": "系统软件包:刷新包管理器(dnf/apt/apk/brew/winget)的软件源索引", + "proc": "本机进程:在后台运行命令并写日志,关掉终端也不退出", +} + +// newGroup builds an intermediate command such as `kopsc disk`. +func newGroup(name string) *cobra.Command { + short, ok := groupDescriptions[name] + if !ok { + short = name + " 相关命令" + } + return &cobra.Command{ + Use: name, + Short: short, + Long: short + "。\n\n运行 " + "kopsc " + name + " --help 查看该组的全部子命令。", + RunE: func(c *cobra.Command, args []string) error { + return c.Help() + }, + } +} + func findChild(parent *cobra.Command, name string) *cobra.Command { for _, child := range parent.Commands() { if child.Name() == name { diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index 37e8960..b518694 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -748,3 +748,55 @@ func readCalls(path string) []string { } return out } + +func TestRootHelpIsChineseAndExplainsGroups(t *testing.T) { + app, stdout, stderr := testApp(centos("docker")) + if code := app.Execute(nil); code != 0 { + t.Fatalf("exit code = %d, stderr=%s", code, stderr.String()) + } + out := stdout.String() + + // The framing cobra would otherwise print in English. + for _, want := range []string{"用法:", "可用的子命令:", "选项:", "显示帮助"} { + if !strings.Contains(out, want) { + t.Errorf("root help missing %q:\n%s", want, out) + } + } + for _, unwanted := range []string{"Usage:", "Available Commands:", "Flags:", "help for ", "[flags]", "Help about any command"} { + if strings.Contains(out, unwanted) { + t.Errorf("root help still contains English %q:\n%s", unwanted, out) + } + } + + // Every group is described in plain words instead of " 相关命令". + for _, want := range []string{ + "磁盘与文件占用", + "网络与端口", + "本机进程", + "系统软件包", + "Docker 容器与镜像管理", + } { + if !strings.Contains(out, want) { + t.Errorf("root help missing group description %q:\n%s", want, out) + } + } + if strings.Contains(out, "相关命令") { + t.Errorf("groups still use the vague \"相关命令\" wording:\n%s", out) + } +} + +func TestGroupHelpIsChinese(t *testing.T) { + app, stdout, stderr := testApp(centos("docker")) + if code := app.Execute([]string{"disk"}); code != 0 { + t.Fatalf("exit code = %d, stderr=%s", code, stderr.String()) + } + out := stdout.String() + for _, want := range []string{"磁盘与文件占用", "用法:", "可用的子命令:", "显示帮助", "kopsc disk --help"} { + if !strings.Contains(out, want) { + t.Errorf("group help missing %q:\n%s", want, out) + } + } + if strings.Contains(out, "help for ") { + t.Errorf("group help still says \"help for\":\n%s", out) + } +} diff --git a/internal/registry/builtin.go b/internal/registry/builtin.go index f0d25cb..3f2d39e 100644 --- a/internal/registry/builtin.go +++ b/internal/registry/builtin.go @@ -323,7 +323,7 @@ func Builtin() *Registry { ID: "disk.usage", Aliases: []string{"disk.du"}, Group: "disk", - Summary: "列出目录下各直接子项的占用空间,按大小降序", + Summary: "看哪个目录或文件最占空间(按大小从大到小排)", Action: "等价于 du -x --max-depth=1 -h <路径> | sort -hr:统计每个直接子项(文件或目录递归)的占用," + "不跨越文件系统边界,末尾输出该路径的合计。路径可省略(默认当前目录),也可作位置参数或用 --path 指定;" + "由于 Windows 没有等价的 du/sort,这里使用 kopsc 内置实现以保证三平台输出一致",