feat(cli): GUI 二进制内建 CLI 子命令分发,ull 纳入发布资产(M82)

一个二进制两种用法:两个 GUI 的 main.rs 在进 UI 之前分发子命令,命中就把控制权交给
unlucky_cli::run —— 不建窗口、不探后端、不扫字体、不 dlopen 图形库。

分发判据只有一条,子命令表不重复:crates/cli 的解析器是唯一真相源。
- 首个 token 是裸词 → 子命令位置,交 CLI(未知子命令由 CLI 报错 + 提示 --help,退出 1,
  不静默开窗)
- 首个 token 是 flag → 只有 CLI 解析出真命令才算 CLI 调用;--data-dir X 解析成 Welcome、
  --screenshot / --renderer / --diff 等 GUI 参数解析失败,一律仍走 GUI(CI 脚本零改动)

顺带:unlucky-launcher --version 以前会直接开窗,现在走 CLI 分发报版本。

packaging:ull 作为独立裸资产随每个槽位发布(make_artifacts.py + release.yml 的 assets
数组与 SHA256SUMS glob),矩阵完整性检查改成 GUI/CLI 各查一次。

app-iced 的 x11 / wayland / linux-theme-detection 收进 target 专属的 cfg(target_os) 段。

门禁实跑:cargo fmt --all --check 通过;cargo clippy --workspace --all-targets -D warnings
通过(exit 0);cargo test --workspace 通过(51 个测试二进制 / 1369 个用例,含本次新增的
5 + 5 条分发判据用例)。
This commit is contained in:
AskaEth committed 2026-11-02 08:08:09 +08:00
1 parent ef3212021a
commit 1c7285cea9
11 files changed
+457 -53

No files matched your search

+49 -10
View File
@@ -43,10 +43,14 @@ Two tracks. They are not the same product yet, so read the headings before downl
already contain them.
- **UI**: Slint + Metro. This is the shipped GUI — the instance, download, account, Java, log and
settings pages all run on the core engine (the in-app AI chat panel is a scripted placeholder).
- **CLI `ull`** is built from the same tree at the same version — but the `v0.1.0` assets ship
only the GUI binary. If you want the CLI, build it yourself:
`cargo build --release --bin ull`. Commands: `instances`, `install`, `launch`, `accounts`,
`sessions`, `logs`, `diag`, `config`, `java`, `agent`.
- **CLI in the same binary**: `unlucky-launcher instances list`, `unlucky-launcher launch <instance>`,
`unlucky-launcher logs <sid>` and the rest of the command set run before any UI is built — no
window, no renderer probe, no font scan. Commands: `instances`, `install`, `launch`, `accounts`,
`sessions`, `logs`, `diag`, `config`, `java`, `agent`, plus `--help` / `--version`.
- **A second, lighter artifact `ull`** (`cargo build --release --bin ull`, zero UI dependencies): the
same command set from a binary that links no graphics stack at all — not even fontconfig. Servers,
CI containers and Termux get `ull`; desktops get `unlucky-launcher` and one binary covers both GUI
and CLI. Both are release assets from the next tagged release (the `v0.1.0` assets predate this).
- Windows ARM64 and Linux ARM64 slots are wired into the release pipeline; the next tagged release
builds them. The `v0.1.0` assets predate those two slots.
@@ -71,9 +75,42 @@ Binaries in the workspace, so you can tell them apart:
| Binary | Crate | State |
|--------|-------|-------|
| `unlucky-launcher` | `crates/app` (Slint) | released in v0.1.0; still the usable GUI |
| `unlucky-launcher-iced` | `crates/app-iced` (iced 0.14) | under construction; skeleton only, not shipped |
| `ull` | `crates/cli` | built from the same tree at the same version; not in the v0.1.0 assets |
| `unlucky-launcher` | `crates/app` (Slint) | released in v0.1.0; still the usable GUI; also runs every CLI subcommand |
| `unlucky-launcher-iced` | `crates/app-iced` (iced 0.14) | under construction; skeleton only, not shipped; same subcommand dispatch |
| `ull` | `crates/cli` | the zero-UI build of the same command set, shipped as its own asset |
`unlucky-launcher` and `ull` are not duplicates. The GUI binary needs the graphics stack when it
opens a window (it `dlopen`s X11 / Wayland / GL, and links `libfontconfig` at load); `ull` needs
nothing beyond glibc, so it runs on a server, a CI container or Termux where no window can exist.
Pick `ull` when there is no desktop, `unlucky-launcher <command>` when there is.
### Subcommand dispatch
`unlucky-launcher <args>` first asks "is this a CLI call?" before any UI code runs:
```bash
unlucky-launcher # GUI
unlucky-launcher --software-renderer # GUI, CPU renderer (unchanged)
unlucky-launcher instances list # CLI, no window
unlucky-launcher --json launch 1.20.1 # CLI, global options before the command
unlucky-launcher --version # CLI, prints version + OS summary
```
The rule is deliberately narrow, so nothing that used to work changed meaning:
- The first argument is a bare word (not `-…`) → it sits in the subcommand position, so the CLI gets
it. GUI flags never appear there: `--screenshot` / `--diff` / `--data-dir` take their values
*after* the flag.
- The first argument is a flag → the line is a CLI call only if `crates/cli`'s parser also produces a
real command (`--help`, `--version`, `--json instances list`). `--data-dir X` alone parses to "no
subcommand", so it still opens the GUI; every GUI-only flag (`--software-renderer`,
`--screenshot*`, `--renderer`, `--theme`, `--diff`, `--force-state`, …) fails CLI parsing, so it
still opens the GUI.
**Unknown subcommands are an error, not a GUI launch**: `unlucky-launcher lst` prints
"未知命令 `lst`", hints `ull --help`, and exits 1. Silently opening a window would tell a script
that its typo took effect. The subcommand table itself lives in exactly one place —
`crates/cli/src/cli/parse.rs` — so both GUI binaries and `ull` accept the same set by construction.
## Module map
@@ -121,11 +158,13 @@ cargo build --release # binaries land in target
What the build produces:
- `unlucky-launcher` — the Slint UI (the released one)
- `unlucky-launcher` — the Slint UI (the released one). It is also the CLI: the same binary answers
every subcommand listed above.
- `unlucky-launcher-iced` — the iced UI skeleton. The name is temporary: it can only become
`unlucky-launcher` at the flip, because two crates in one workspace cannot emit the same binary
name into the same target directory.
- `ull` — the CLI
name into the same target directory. Same subcommand dispatch.
- `ull` — the CLI as a standalone, UI-free binary. Same command set, no graphics stack, smaller, and
the right artifact for a machine that has no display at all.
Rendering switches: