feat: 新增 disk usage 内置命令(跨平台 du 等价)

- 注册表新增 Variant.Builtin 内置动作,与 Argv 互斥并参与平台匹配
- disk.usage 支持位置参数与 --path/--path=,输出按大小降序并带合计
- Unix 用设备号实现 -x 不跨文件系统,Windows 降级为不跟随 reparse point
- list/help/explain 支持展示内置动作,补充单元与 CLI 测试、README
This commit is contained in:
2026-10-31 13:11:24 +08:00
parent 7d9852958f
commit 84f7361396
12 changed files with 672 additions and 9 deletions
+12 -4
View File
@@ -111,6 +111,14 @@ func findChild(parent *cobra.Command, name string) *cobra.Command {
return nil
}
// variantDisplay renders how a variant is executed, for list/help/explain.
func variantDisplay(v registry.Variant) string {
if v.Builtin != "" {
return "(内置) " + v.Builtin
}
return strings.Join(v.Argv, " ")
}
func (a *App) newLeaf(cmd *registry.Command, name string) *cobra.Command {
leaf := &cobra.Command{
Use: name + " [--dry-run] [-h] [-- 透传参数...]",
@@ -174,7 +182,7 @@ func (a *App) printLeafHelp(w io.Writer, cmd *registry.Command) {
fmt.Fprintf(w, "当前平台: %s\n", resolve.DescribeInfo(a.Info))
if result, err := resolve.Resolve(cmd, a.Info); err == nil {
fmt.Fprintf(w, "将执行: %s\n\n", strings.Join(result.Variant.Argv, " "))
fmt.Fprintf(w, "将执行: %s\n\n", variantDisplay(result.Variant))
} else {
fmt.Fprintf(w, "当前平台不可用: %v\n\n", err)
}
@@ -197,7 +205,7 @@ func (a *App) newListCmd() *cobra.Command {
entry := &a.Registry.Commands[i]
status, planned := "可用", "-"
if result, err := resolve.Resolve(entry, a.Info); err == nil {
planned = strings.Join(result.Variant.Argv, " ")
planned = variantDisplay(result.Variant)
} else {
status = "不可用"
}
@@ -230,7 +238,7 @@ func (a *App) newExplainCmd() *cobra.Command {
fmt.Fprintf(w, "结果: 当前平台不可用 — %v\n\n", err)
} else {
fmt.Fprintf(w, "结果: 命中变体 #%d(%d 分)→ %s\n\n",
result.VariantIdx, result.Score, strings.Join(result.Variant.Argv, " "))
result.VariantIdx, result.Score, variantDisplay(result.Variant))
}
fmt.Fprintln(w, "候选:")
@@ -244,7 +252,7 @@ func (a *App) newExplainCmd() *cobra.Command {
note = " # " + c.Variant.Note
}
fmt.Fprintf(w, " %s #%d %d 分 %s -> %s%s\n",
mark, c.Index, c.Score, c.Reason, strings.Join(c.Variant.Argv, " "), note)
mark, c.Index, c.Score, c.Reason, variantDisplay(c.Variant), note)
}
return nil
},
+75
View File
@@ -3,6 +3,7 @@ package cli
import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
@@ -241,6 +242,80 @@ func TestParseLeafArgs(t *testing.T) {
}
}
func TestDiskUsageCommandThroughCLI(t *testing.T) {
root := t.TempDir()
if err := os.WriteFile(filepath.Join(root, "a.bin"), bytes.Repeat([]byte("x"), 4096), 0o644); err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(filepath.Join(root, "sub"), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(root, "sub", "b.bin"), bytes.Repeat([]byte("x"), 1024), 0o644); err != nil {
t.Fatal(err)
}
app, stdout, stderr := testApp(centos("docker"))
if code := app.Execute([]string{"disk", "usage", root}); code != 0 {
t.Fatalf("exit code = %d, stderr=%s", code, stderr.String())
}
out := stdout.String()
for _, want := range []string{"4.0K", "1.0K", "a.bin", "sub", "总计 " + root} {
if !strings.Contains(out, want) {
t.Errorf("disk usage output missing %q:\n%s", want, out)
}
}
}
func TestDiskUsageAcceptsPathFlagThroughCLI(t *testing.T) {
root := t.TempDir()
app, stdout, stderr := testApp(centos("docker"))
if code := app.Execute([]string{"disk", "usage", "--path", root}); code != 0 {
t.Fatalf("exit code = %d, stderr=%s", code, stderr.String())
}
if !strings.Contains(stdout.String(), "总计 "+root) {
t.Errorf("unexpected output:\n%s", stdout.String())
}
}
func TestDiskUsageRequiresPathThroughCLI(t *testing.T) {
app, _, stderr := testApp(centos("docker"))
if code := app.Execute([]string{"disk", "usage"}); code != 1 {
t.Fatalf("exit code = %d, want 1", code)
}
if !strings.Contains(stderr.String(), "必须提供路径") {
t.Errorf("stderr = %s", stderr.String())
}
}
func TestDiskUsageDryRunDoesNotTouchPath(t *testing.T) {
app, stdout, _ := testApp(centos("docker"))
code := app.Execute([]string{"disk", "usage", "/path/that/does/not/exist", "--dry-run"})
if code != 0 {
t.Fatalf("exit code = %d", code)
}
out := stdout.String()
if !strings.Contains(out, "(内置) disk.usage /path/that/does/not/exist") {
t.Errorf("dry-run output missing builtin plan:\n%s", out)
}
if strings.Contains(out, "无法访问") {
t.Errorf("dry-run should not execute the builtin:\n%s", out)
}
}
func TestDiskUsageHelp(t *testing.T) {
app, stdout, _ := testApp(centos("docker"))
if code := app.Execute([]string{"disk", "usage", "-h"}); code != 0 {
t.Fatalf("exit code = %d", code)
}
if !strings.Contains(stdout.String(), "(内置) disk.usage") {
t.Errorf("help output missing builtin target:\n%s", stdout.String())
}
}
// TestCLIHelperProcess is re-executed as a child to verify exit-code passthrough.
func TestCLIHelperProcess(t *testing.T) {
if os.Getenv("KOPSC_CLI_HELPER") != "1" {