fix(m163): push tag 默认建稳定版(发布标记语义 + 0.2.3 bump)
发布标记:`release.yml` 的 dispatch `prerelease` 默认值 true → false;`release-publish.yml`
的 create 路径不再塞 `--prerelease`(空串 = push tag ⇒ 稳定版),edit 路径行为不变(空串不碰
标记)。三种触发各创建什么改由 `packaging/release_marker.py` 一张表定义,流水线
`--emit-bash` 出来两个数组直接用,自己不再写 case——v0.2.2 至今 `releases/latest` 404 的根因
就是「push 默认 prerelease」这层叠上「dispatch 默认 true」。
守门:新增 `packaging/check_release_semantics.py`(CI 在 `.github/workflows/release-guard.yml`,
guards.yml 已 398 行不能再挂 job):①标记表逐条对(push tag 的 create 不含 --prerelease);
②把 `--emit-bash` 真灌进 bash 读回两个数组;③publish 的可执行内容里没有 --prerelease 字面量、
必须调用 emit-bash、两个数组各喂 create/edit;④release.yml 的 dispatch 默认值 == false(读
YAML 结构)。`--self-mutation` 注入三项真实回归(默认值改回 true / 塞回字面量 / 表里 push
改成预发布),断言每项都红、基线绿。
版本:0.2.2 → 0.2.3(workspace `[workspace.package]` + 五处 path 依赖 pin + Cargo.lock)。
文档:README 中英两版版本行改 0.2.3,并把本阶段能力补进现有章节(Modrinth 匿名在线内容源、
自更新与稳定通道、整合包导入逐文件决策、实例级启动参数),删掉「没有自动更新通道」「导入只有
两份全局策略」两条过期说法;`release-notes.md` 写 0.2.3 的真实更新摘要(保留 {{VERSION}} /
{{SUMS}});`packaging/README.md` 与 `docs/build-and-ci.md` 里「push 默认仍 prerelease」的旧口径
全部改掉。
This commit is contained in:
1 parent
5d5fe8aa73
commit
1d2e84ed61
18 files changed
+805
-74
No files matched your search
@@ -4,6 +4,8 @@
|
||||
|
||||
> **Official project home**: <https://github.com/AskaEth/Unlucky-Launcher> | downloads, issues and
|
||||
> release notes all live there: <https://github.com/AskaEth/Unlucky-Launcher/releases>
|
||||
>
|
||||
> **Version in this README: `0.2.3`** — `unlucky-launcher --version` prints the same number.
|
||||
|
||||
Unlucky Launcher is a free, open-source **Minecraft: Java Edition** launcher written in Rust
|
||||
(Apache-2.0). It ships as **one native binary per platform** — no installer, no bundled runtime, no
|
||||
@@ -43,6 +45,14 @@ gap: the reason text is still raw Chinese straight out of the launch layer (see
|
||||
- **Launching** — a 10-stage command-line assembly (`crates/core/src/launch/`), per-instance JVM and
|
||||
game arguments, a native-library pass, and a JSONL session record for every run. Process trees are
|
||||
killed as trees, not as one PID.
|
||||
- **Per-instance launch arguments** — JVM arguments, game arguments, memory, the Java executable and
|
||||
the graphics backend (`default` / `opengl` / `vulkan`) live in the instance file under `launch.*`,
|
||||
and every field falls back on its own: instance → global setting → factory default, so an instance
|
||||
that only sets memory does not wipe the global JVM arguments. JVM arguments are *appended* (global
|
||||
first, instance last, so the JVM's last-one-wins rule makes the instance win). An out-of-range
|
||||
memory value counts as "not set" instead of producing `-Xmx0m`, an empty field means "follow", and
|
||||
the backend row only appears for game versions that have it. The CLI and the GUI read the same
|
||||
resolved values (`crates/core/src/game/launch_overrides.rs`).
|
||||
- **Crash diagnosis is real and local** — `crates/core/src/diagnose` collects five kinds of signals
|
||||
(exit code, `latest.log`, `crash-reports`, `hs_err_pid` files, environment snapshot including the
|
||||
mod list), matches them against a YAML rule set, and shows the findings with fix actions on the
|
||||
@@ -56,8 +66,17 @@ gap: the reason text is still raw Chinese straight out of the launch layer (see
|
||||
export as `.mrpack` or a CurseForge manifest. Cancelling an **import** is interface-level only: the
|
||||
panel closes and the result is discarded, but the import thread runs to the end, because `core`'s
|
||||
import request has no cancel token yet (`crates/presenter/src/host/addon/pack.rs:28-42`).
|
||||
- **Import conflicts are decided per file** — before anything is written, the scan lists every
|
||||
conflicting file (what it is, why it conflicts, how big it is) in pages of 40, and three entries
|
||||
decide the batch: keep all, overwrite all, or choose per item — unpicked rows stay on "keep". Files
|
||||
that do not conflict are never rewritten, and the report still names every conflict afterwards.
|
||||
- **Content library** — modpacks / mods / resource packs / shaders with keyword, source, version,
|
||||
loader and sort filters, paged results, and one-click *install into this instance*.
|
||||
- **Online source: Modrinth, anonymously** — search goes through `/v2/search` with facets taken from
|
||||
the target instance (project type, game version, loader); project details and version lists follow,
|
||||
and a remote entry whose hash is known is completed through `/v2/version_file/<hash>`. No API key,
|
||||
and no POST: the download engine has no request-body field, so hashes are looked up one GET at a
|
||||
time. Being offline is its own state — "not connected" — never "no matching content".
|
||||
- **What that page will refuse, and says so on the row** — a modpack cannot be installed *from* the
|
||||
library (packs go through the import pipeline above); CurseForge's *install* is disabled because
|
||||
its file endpoint is not wired; and an item whose game version or loader does not match the target
|
||||
@@ -160,14 +179,42 @@ The dispatch rule is deliberately narrow, so nothing that used to work changed m
|
||||
that its typo took effect. The subcommand table lives in exactly one place
|
||||
(`crates/cli/src/cli/parse.rs`), so both binaries accept the same set by construction. `ull --help`
|
||||
lists them all: `instances`, `install`, `launch`, `pack import`, `accounts`, `sessions`, `logs`,
|
||||
`diag`, `config`, `java`, `agent`, `version`, `help`.
|
||||
`diag`, `config`, `java`, `update`, `agent`, `version`, `help`.
|
||||
|
||||
### Updates, and the stable channel
|
||||
|
||||
- **Self-update is real, and GUI and CLI share one core.** The settings panel's Update group and
|
||||
`ull update check` / `ull update apply` both build the same `core::update::Job`
|
||||
(`crates/core/src/update/`): fetch the manifest at `releases/latest/download/manifest.json`, compare
|
||||
versions, admit the channel, verify length + SHA256, then swap the binary atomically — rename the
|
||||
old file aside, move the new one in, roll back if that fails. A running Windows exe can be
|
||||
*renamed* but not overwritten, which is why the swap is a rename.
|
||||
- **Four outcomes, never conflated**: up to date / update available / a newer version exists but ships
|
||||
no artifact for this platform / the check itself failed (offline, broken manifest, channel
|
||||
mismatch). A failed check is never reported as "up to date".
|
||||
- **Only the stable channel is published.** `update.channel` defaults to `stable`; beta / alpha /
|
||||
nightly are modelled and reserved. A manifest is admitted only when both its declared channel and
|
||||
the version's own pre-release stage agree — a manifest can lie, a version number cannot.
|
||||
- **Why a release has to be marked stable**: `releases/latest/download/*` only points at the newest
|
||||
**non-prerelease** release, and the manifest address is pinned there. `0.2.3` is the first release
|
||||
created without the pre-release flag (`0.2.2` stays a historical pre-release). Which trigger creates
|
||||
which marker — push tag ⇒ stable, dispatch `prerelease=true` ⇒ pre-release — is one table
|
||||
(`packaging/release_marker.py`), with a guard over it (`packaging/check_release_semantics.py`).
|
||||
- **Nothing installs itself behind your back.** The check at startup can be turned off
|
||||
(`update.auto_check`, the same key the CLI reads), and installing is a button or an explicit
|
||||
`ull update apply`. Debug builds can rehearse the whole chain against a local directory source
|
||||
(`ULL_UPDATE_DIR`); release builds do not contain that path at all.
|
||||
|
||||
### Not done yet
|
||||
|
||||
This list is the reason to believe the rest. None of it is hidden behind marketing copy.
|
||||
|
||||
- **No installer and no auto-update channel.** You download a file and run it. Nothing checks for a
|
||||
newer version for you, and there is no update source decided yet.
|
||||
- **No installer.** You download a file and run it — there is no setup step, and updates replace that
|
||||
file in place instead of installing anything (see [Updates](#updates-and-the-stable-channel)).
|
||||
- **The update path has only been exercised against a local directory source.** The whole chain
|
||||
(manifest → download → SHA256 → rename/rollback) ran with a debug build and a fixture, because the
|
||||
real `releases/latest/download/manifest.json` was unreadable while `0.2.2` was still a pre-release.
|
||||
The first observation on the live stable channel happens after `0.2.3` ships.
|
||||
- **Several buttons are placeholders.** On the instances page: *New instance*, *Import*, *Duplicate*,
|
||||
*Open folder*, *Delete*. On the logs page: *Open folder*. In the settings frame: *Save*, *Reset
|
||||
section*, *Add override*. Each writes `[page] 占位动作 …` to stderr and changes nothing — instance
|
||||
@@ -190,8 +237,6 @@ This list is the reason to believe the rest. None of it is hidden behind marketi
|
||||
launch layer (`crates/core/src/launch/exit_analysis.rs`) instead of a reason code the UI could
|
||||
translate — which is exactly the `静默失败` you can see in the English screenshot above.
|
||||
- **The in-app assistant has no model behind it** (see above). The MCP server does.
|
||||
- **Pack import has two global policies**, keep-user or overwrite, not a per-file choice; the import
|
||||
report lists every conflict so nothing is overwritten silently.
|
||||
- **Install progress is stage-level**, not byte-level, on the content-library install path.
|
||||
- **No native file chooser.** Import takes drag-and-drop or a pasted path.
|
||||
- **Android/Termux is experimental and not promised**: no released asset targets it. A GUI build is
|
||||
@@ -297,6 +342,7 @@ merge. The macOS probe is not there yet — manual dispatch only, result recorde
|
||||
| `.github/workflows/ci.yml` | fmt + clippy + test + release build (Windows / Linux matrix); Linux runtime smoke (Xvfb, one real frame, PNG asserted non-blank); macOS runtime probe (manual dispatch only) |
|
||||
| `.github/workflows/guards.yml` | zero-UI guards (`crates/core`, `crates/presenter`, `crates/design`), 400-lines-per-file guard, cargo-deny licence and source guard |
|
||||
| `.github/workflows/release.yml` | tag `v*` → build both Windows slots (`windows-x64` + `windows-arm64`) on native runners → single-file artifacts + `SHA256SUMS.txt` → GitHub Release (publishes `windows-x64` only; `-f all_slots=true` builds all six slots for evidence) |
|
||||
| `.github/workflows/release-guard.yml` | release-marker semantics: push tag ⇒ a **stable** release, dispatch `prerelease=true` ⇒ pre-release; the table lives in `packaging/release_marker.py` and the guard mutation-tests itself |
|
||||
|
||||
## Architecture and module map
|
||||
|
||||
|
||||
Reference in new issue
Block a user