feat: 新增 java run,按 JDK 版本自动套用调优参数运行 Java 应用

- 自动识别 JDK 8/11/17/21 与宿主机 CPU、可用内存;容器内以 cgroup
  限额为准;堆默认取可用内存 50%,-Xms 与 -Xmx 同值
- 按版本分支生成参数:JDK 8 用 -Xloggc 一组,JDK 9+ 用 -Xlog:;
  堆超过 30g 时自动省略压缩指针参数
- 支持 --jdk/--java/--heap/--xms/--env/--gc-log/--heap-dump-on-oom/
  --print-flags/--dry-run;jar 之后的参数原样交给应用
- 前台运行:stdio 直连、退出码透传、SIGINT/SIGTERM 转发给 JVM
- BuiltinRequest 增加 Stdin,使内置命令可以读取调用方输入
- 修正帮助中的用法行重复(自定义 Usage 不再追加通用透传尾巴)
- 文档:新增 README.java.md,主 README 补充导航与示例
This commit is contained in:
2026-11-01 09:16:19 +08:00
parent 14817bcfe7
commit f62c1f4872
16 changed files with 2139 additions and 7 deletions
+26 -4
View File
@@ -221,6 +221,7 @@ var groupDescriptions = map[string]string{
"net": "网络与端口:端口被谁占用、本机监听了哪些端口",
"pkg": "系统软件包:刷新包管理器(dnf/apt/apk/brew/winget)的软件源索引",
"proc": "本机进程:在后台运行命令并写日志,关掉终端也不退出",
"java": "Java 应用:按 JDK 版本自动套用调优参数并前台运行 jar",
}
// newGroup builds an intermediate command such as `kopsc disk`.
@@ -256,9 +257,19 @@ func variantDisplay(v registry.Variant) string {
return strings.Join(v.Argv, " ")
}
// leafUsage renders the cobra usage line for a leaf command. Commands that
// already spell out their own arguments (such as `java run <app.jar> [-- 应用参数...]`)
// keep it as-is; the generic "[-- 透传参数...]" tail would only repeat it.
func leafUsage(name, usage string) string {
if strings.TrimSpace(usage) != "" {
return name + " " + usage
}
return name + " [--dry-run] [-h] [-- 透传参数...]"
}
func (a *App) newLeaf(cmd *registry.Command, name string) *cobra.Command {
leaf := &cobra.Command{
Use: name + " [--dry-run] [-h] [-- 透传参数...]",
Use: leafUsage(name, cmd.Usage),
Short: cmd.Summary,
Long: cmd.Summary + "\n\n动作: " + cmd.Action,
DisableFlagParsing: true,
@@ -287,6 +298,15 @@ func (a *App) newLeaf(cmd *registry.Command, name string) *cobra.Command {
return a.printConfirmation(cmd, plan)
}
if dryRun {
// A command with a preview builtin can show exactly what it
// would do (for example the final java command line), so
// render that instead of only the builtin's name.
if err := executor.Preview(context.Background(), plan, a.Stdout); err != nil {
return err
}
}
return plan.Run(context.Background(), executor.Options{
DryRun: dryRun,
Stdin: a.Stdin,
@@ -379,11 +399,13 @@ func parseLeafArgs(cmd *registry.Command, args []string) (dryRun, yes bool, extr
func (a *App) printLeafHelp(w io.Writer, cmd *registry.Command) {
invocation := cmd.Invocation()
argsHint := ""
// A command that documents its own arguments already ends with them, so
// the generic passthrough tail is only added when there is no Usage.
if cmd.Usage != "" {
argsHint = " " + cmd.Usage
fmt.Fprintf(w, "用法: %s [--dry-run] [-h] [--yes] %s\n\n", invocation, cmd.Usage)
} else {
fmt.Fprintf(w, "用法: %s [--dry-run] [-h] [--yes] [-- 透传参数...]\n\n", invocation)
}
fmt.Fprintf(w, "用法: %s [--dry-run] [-h] [--yes]%s [-- 透传参数...]\n\n", invocation, argsHint)
fmt.Fprintf(w, "%s\n\n", cmd.Summary)
fmt.Fprintf(w, "动作: %s\n\n", cmd.Action)
fmt.Fprintf(w, "当前平台: %s\n", resolve.DescribeInfo(a.Info))