From 54b81cbb7f5874d83fe6938c5d1795ffcf691d21 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Thu, 5 Nov 2026 21:08:33 +0800 Subject: [PATCH] =?UTF-8?q?chore(release):=20M137=20=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8D=87=E5=88=B0=200.2.1=EF=BC=8C=E5=8F=91=E5=B8=83=E5=8F=AA?= =?UTF-8?q?=E5=8F=91=E4=B8=A4=E4=B8=AA=20Windows=20=E6=A7=BD=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 版本:`[workspace.package] version` 0.2.0 → 0.2.1,并把全部 intra-workspace path 依赖的 version 约束一并对齐(`"0.2"` → `"0.2.1"`,M121 的教训)。Cargo.lock 里只有 8 个 unlucky-* 成员条目变化。`crates/app` 是 workspace `exclude` 的 Slint 退役树(manifest 连解析都过不了),不参与解析,故原样保留。 release.yml:新增 `PUBLISH_SLOTS` 白名单(唯一开关)= `windows-x64 windows-arm64`。 六槽位一律构建取证,release job 先按白名单裁掉 Linux/macOS 四档产物再重算 SHA256SUMS, 所以 tag 触发的 Release 只有 4 个资产 + SHA256SUMS.txt。release job 改成 `always() && !cancelled()`:未验证槽位构建失败不连坐 Windows 发布,真正的门禁是白名单 齐全性检查。将来某槽位真机验过后把它加进那一行即打开。 顺手修掉一处从未生效的门禁:齐全性检查的 glob 用的是连字符形态 (`dist/$prefix-*-$slot*`),对 D49 之后的下划线资产名永远匹配不上,改成 `dist/${prefix}_*_${stem}*`。资产命名规则 / SHA256SUMS / 合规文本并列资产 / `--version` 与 tag 交叉校验一律未动。 packaging/release-notes.md:清掉 v0.1.0 / Slint 时代的说明,Downloads 与 Support matrix 只描述本版实际发布的 Windows 两个槽位(其余标「built by CI, not published」)。 packaging/README.md §1/§3/§9 同步 gate(矩阵加「本版发布」列、§3 写清白名单与打开办法)。 起手顺带把示例 tag 从 v0.2.0 更到 v0.2.1,并把「winit / femtovg」更正为「winit / wgpu」。 --- .github/workflows/release.yml | 135 ++++++++++++++++++------------ Cargo.lock | 16 ++-- Cargo.toml | 2 +- crates/agent/Cargo.toml | 2 +- crates/app-iced/Cargo.toml | 8 +- crates/cli/Cargo.toml | 4 +- crates/palette-extract/Cargo.toml | 2 +- crates/presenter/Cargo.toml | 2 +- packaging/README.md | 95 ++++++++++----------- packaging/release-notes.md | 87 +++++++++---------- 10 files changed, 192 insertions(+), 161 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 43ea686..b8df0f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,17 +1,21 @@ name: Release -# 发布流水线:tag `v*` → 四平台(六槽位)矩阵构建 → 单文件产物 + SHA256SUMS +# 发布流水线:tag `v*` → 四平台(六槽位)矩阵**全部构建** → 单文件产物 + SHA256SUMS # → 上传 artifact + 创建 GitHub Release(pre-release 默认开)。同一 tag 重跑幂等: # Release 已存在时覆盖资产与说明,不再 `gh release create`(见 release job 末步); # 重跑**不碰** pre-release 标记,该标记只在 `inputs.prerelease` 显式给出时才被断言。 # -# 契约唯一真相源:packaging/README.md(命名/目录/运行时依赖)。 +# **构建 ≠ 发布**:六槽位一律构建取证,但只有 release job 的 `PUBLISH_SLOTS` 白名单里的槽位会进 Release +# 与 SHA256SUMS。当前只有两个 Windows 槽位 —— Linux/macOS 四档从未真构建成功过一次、也没在真机跑过, +# 未验证的产物不推给用户(开关与打开办法见该处注释)。 +# +# 契约唯一真相源:packaging/README.md(命名/目录/运行时依赖/发布白名单)。 # 打包本体在 packaging/make_artifacts.py(本地与 CI 同一份脚本,逐字节同布局)。 -# 手动触发(workflow_dispatch)默认只跑构建不建 Release —— 用来验证矩阵仍然编得过。 -# 注意 `inputs.prerelease` 只在 **tag ref** 上 dispatch 才可达:Actions 页面的 -# "Run workflow" 只能选分支,要跑 tag 得 `gh workflow run release.yml --ref v0.1.0`。 -# 分支 ref 的 dispatch 与 push 事件一样被 release job 的 `if: github.ref_type == 'tag'` -# 挡在发布之外,所以「dispatch 只构建不发布」与「dispatch 可关 pre-release」并不矛盾。 +# 手动触发(workflow_dispatch)默认只跑构建不建 Release —— 用来把六槽位矩阵整体编一遍取证。 +# `inputs.prerelease` 只在 **tag ref** 上 dispatch 才可达(Actions 页面只能选分支,要跑 tag 得 +# `gh workflow run release.yml --ref v0.2.1`);分支 ref 的 dispatch 与 push 一样被 release job 的 +# `if: github.ref_type == 'tag'` 挡在发布之外,所以「dispatch 只构建不发布」与「dispatch 可关 +# pre-release」并不矛盾。 on: push: @@ -50,31 +54,27 @@ jobs: build_dir: target/release rustflags: -C target-feature=+crt-static # Windows arm64:在 windows-latest(x64 宿主)上**交叉编译**到 aarch64-pc-windows-msvc。 - # 为什么不用 windows-11-arm 原生 runner(GitHub 文档 2026 已把 `windows-11-arm` 与 - # `windows-11-vs2026-arm` 列为公开仓库**免费**的标准 arm64 runner,原生路线可行): - # ① 双 Windows 槽位共用同一套已验证镜像/工具链,tag 重跑行为与既有槽位一致; - # ② M50 的 dumpbin 定位链取的是 `Hostx64/x64/dumpbin.exe`,dumpbin 读 ARM64 PE 的 - # 导入表没问题,而原生 ARM 宿主上那条链得再长一条 `Hostarm64/arm64` 分支——为一个 - # 槽位放宽刚立的门禁不值得; - # ③ 交叉编译产物路径变成 target//release,已用 matrix.build_dir 参数化。 - # 若将来改成原生:只需把 os 换成 windows-11-arm、删掉 target/rustflags 依赖并给 dumpbin - # 定位链补 Hostarm64 分支(届时再评估,不在这里预先放宽)。 + # 不用 windows-11-arm 原生 runner(GitHub 文档 2026 已把它列为公开仓库免费 runner)的理由: + # ① 双 Windows 槽位共用同一套已验证镜像/工具链,tag 重跑行为与既有槽位一致;② M50 的 dumpbin + # 定位链取 `Hostx64/x64/dumpbin.exe`,它读 ARM64 PE 的导入表没问题,而原生 ARM 宿主上那条链 + # 还得再长一条 `Hostarm64/arm64` 分支——为一个槽位放宽刚立的门禁不值得;③ 交叉产物路径变成 + # target//release,已用 matrix.build_dir 参数化。将来改原生:os 换 windows-11-arm、 + # 去掉 target/rustflags 依赖并给 dumpbin 定位链补 Hostarm64 分支(届时再评估)。 - slot: windows-arm64 os: windows-latest target: aarch64-pc-windows-msvc build_dir: target/aarch64-pc-windows-msvc/release rustflags: -C target-feature=+crt-static + # ↓ 下面四档(Linux x64/arm64、macOS arm64/x64)只**构建取证**、不进本次 Release(从未真构建 + # 成功过一次、也没在真机跑过);发哪几个槽位由 release job 的 `PUBLISH_SLOTS` 白名单唯一决定。 # Linux:固定 ubuntu-22.04(glibc 2.35 地板)而不是 latest, # 否则产物的 glibc 底线会跟着 runner 漂移(24.04 的 glibc 明显更高)。 - slot: linux-x64 os: ubuntu-22.04 build_dir: target/release - # Linux arm64:公开仓库免费的原生 ARM64 runner(原生构建,不做交叉)。 - # 用 `ubuntu-22.04-arm` 而**不是** 24.04:与 x64 同 glibc 地板(2.35), - # 覆盖 Debian 12 / Ubuntu 22.04 arm64 / Raspberry Pi OS Bookworm(2.36)这类实际在跑的 - # ARM 发行版;换 24.04 会把地板抬高、把上面前两个挡在门外。该家族标签有 - # `ubuntu-22.04-arm` / `ubuntu-24.04-arm` / `ubuntu-26.04-arm`,均 4C/16GB、 - # 公开仓库免费不限量(GitHub 文档 Standard runners for public repositories)。 + # Linux arm64:公开仓库免费的原生 ARM64 runner(4C/16GB,原生构建不交叉)。用 22.04-arm + # 而非 24.04 是为了与 x64 同 glibc 地板(2.35),覆盖 Debian 12 / Ubuntu 22.04 arm64 / + # Raspberry Pi OS Bookworm(2.36);该家族标签还有 `ubuntu-24.04-arm` / `ubuntu-26.04-arm`。 - slot: linux-arm64 os: ubuntu-22.04-arm build_dir: target/release @@ -89,7 +89,7 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Linux 图形栈构建依赖(winit / femtovg 编译期需要) + - name: Linux 图形栈构建依赖(winit / wgpu 编译期需要) if: runner.os == 'Linux' shell: bash run: | @@ -101,8 +101,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - # 只有交叉编译槽位需要额外 target(原生槽位留空 → 本步整体跳过)。 - # 不把 `${{ }}` 直接拼进 bash:与后面的打包步骤同一纪律,一律经 env 传入再展开。 + # 只有交叉槽位需要额外 target(原生槽位留空 → 本步跳过);`${{ }}` 一律经 env 传入再展开,不拼进 bash。 - name: 追加交叉编译目标 ${{ matrix.target }} if: matrix.target != '' shell: bash @@ -110,9 +109,8 @@ jobs: TARGET: ${{ matrix.target }} run: rustup target add "$TARGET" - # 交叉链接的前提是镜像里真有 ARM64 的 MSVC 库(VC `/lib/arm64/libcmt.lib`,+crt-static - # 就靠它)与 Windows SDK 的 ARM64 导入库(`um/arm64/kernel32.lib`)。缺了照样是失败, - # 但先给一句能照做的话,而不是让链接器吐一屏乱码才发现方向:门禁不降级,只把失败说清楚。 + # 交叉链接的前提是镜像里真有 ARM64 的 MSVC 库(VC `/lib/arm64/libcmt.lib`,+crt-static 就靠它) + # 与 SDK 的 ARM64 导入库(`um/arm64/kernel32.lib`):缺了照样失败,只是先给一句能照做的话。 - name: Windows ARM64 交叉工具链体检 if: matrix.target == 'aarch64-pc-windows-msvc' shell: bash @@ -132,13 +130,11 @@ jobs: exit 1 fi - # M118(D26):**Slint 版停发**。旧写法 `--bin unlucky-launcher` 打的是 `crates/app` - # (Slint 退役树)的 bin —— 也就是说这条流水线一直在发 Slint 版二进制,而 D26 早已把 - # iced 定为产品 UI。现在 GUI 来源只有 `unlucky-launcher-iced`;`crates/app` 也已在根 + # M118(D26):**Slint 版停发** —— GUI 来源只有 `unlucky-launcher-iced`;`crates/app` 已在根 # Cargo.toml 被 `exclude`,不参与任何 workspace 构建(它连 `--manifest-path` 都编不了)。 - # 对外产物名由 `packaging/make_artifacts.py` 的 `asset_stem()` 统一生成,格式是 - # `<名字>_<版本号>_<平台>_<架构>`(用户令 2026-11-05):下划线分段,且**不带实现细节 - # 字样**(产物名里不出现 `iced`)。`LAUNCHER_BIN` 只用来在构建产物里找文件。 + # 对外产物名由 packaging/make_artifacts.py 的 `asset_stem()` 统一生成 + # `<名字>_<版本号>_<平台>_<架构>`(下划线分段、不带 `iced` 这类实现细节字样);`LAUNCHER_BIN` + # 只用来在构建产物里找文件。 - name: cargo build --release --locked shell: bash env: @@ -157,20 +153,16 @@ jobs: shell: bash env: # 跨槽位的关键点:产物在 target//release 而不是 target/release。 - # M118:校验的是**构建产物**(iced bin 的原始文件名)。对外名 `unlucky-launcher` - # 由 packaging/make_artifacts.py 在落盘时才换上,那一步之后才存在。 + # M118:校验的是**构建产物**(iced bin 的原始文件名);对外名 `unlucky-launcher` 由 + # packaging/make_artifacts.py 在落盘时才换上,那一步之后才存在。 EXE: ${{ matrix.build_dir }}/unlucky-launcher-iced.exe run: | set -euo pipefail - # +crt-static 是否真的生效,只看导入表:出现 VCRUNTIME/MSVCP/msvcr/ucrtbase 说明还得装 - # 可再发行包,那就不是"单文件"了,直接 fail。 + # +crt-static 是否真生效只看导入表:出现 VCRUNTIME/MSVCP/msvcr/ucrtbase 就是还得装可再发行包,fail。 # # dumpbin 定位链(三级,全不命中才 fail —— 门禁不降级,只是把"猜路径"换成官方定位): - # ① vswhere:VS 官方定位器,安装器自身路径固定,与 VS 版本/版次/安装布局无关; - # ② find 兜底:Program Files 与 Program Files (x86) 两个 VS 根都扫(Build Tools 落在 x86 根); - # ③ PATH:镜像若已把工具链预置进 PATH 则直接可用。 - # PATH 排最后是刻意的:PATH 最不可控,官方定位器优先。 - # Hostx64 的 dumpbin 读 ARM64 PE 的导入表没问题,所以 arm64 槽位共用同一条链。 + # ① vswhere(VS 官方定位器,安装器路径固定)② find 兜底(两个 VS 根都扫,Build Tools 在 + # x86 根)③ PATH(镜像预置则直接可用;排最后是刻意的:PATH 最不可控,官方定位器优先)。 VSWHERE="C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe" DUMPBIN="" VIA="" @@ -208,8 +200,7 @@ jobs: VIA="PATH" fi fi - # 工具缺失同样 fail:windows-latest 自带 VS 工具链,基本找得到; - # "找不到就跳过"等于让这道门禁在最需要它的时候静默失效。 + # 工具缺失同样 fail:"找不到就跳过"等于让这道门禁在最需要它的时候静默失效。 if [ -z "$DUMPBIN" ]; then echo "::error::找不到 dumpbin,无法校验导入表(静态 CRT 门禁不降级)" exit 1 @@ -272,10 +263,21 @@ jobs: release: name: GitHub Release needs: build - if: github.ref_type == 'tag' + # `always()` + `!cancelled()`:白名单外的槽位(Linux/macOS)只是构建取证,它们红了**不能连坐** + # Windows 发布。真正拦发布的是下面「发布白名单齐全性」那一步:白名单里少任何一个就 fail。 + if: always() && !cancelled() && github.ref_type == 'tag' runs-on: ubuntu-latest permissions: contents: write + env: + # ── 发布白名单:唯一决定「哪几个槽位进 Release」的开关(空格分隔,键同 matrix.slot)── + # 为什么只有两个 Windows 槽位:Linux x64/arm64、macOS arm64/x64 这四档**从未真构建成功过一次、 + # 更没在真机上跑过**,未验证的产物不推给用户;它们照跑构建,产物留在本次运行的 `dist-` + # artifact 里取证(可下载验证),不进 Release、不进 SHA256SUMS。 + # 将来某槽位真机验证通过后「打开」它只改这一行(把槽位键加进列表,如 `macos-arm64`):齐全性检查 + # 与 SHA256SUMS 自动跟着扩;同时把 packaging/README.md §1/§9 与 packaging/release-notes.md 的 + # Downloads / Support matrix 同步成「已发布」——三处不一致即是假话。 + PUBLISH_SLOTS: "windows-x64 windows-arm64" steps: - uses: actions/checkout@v4 @@ -284,23 +286,48 @@ jobs: path: dist merge-multiple: true - - name: 矩阵完整性 + 重算全量 SHA256SUMS + - name: 裁掉白名单外的槽位产物(构建取证 ≠ 发布) + shell: bash + run: | + set -euo pipefail + # 槽位键 `windows-x64` 落到文件名是 `windows_x64`(make_artifacts.py::asset_stem),故取文件名里的 + # 「平台_架构」两段跟白名单比:`<名字>_<版本>_<平台>_<架构>[.exe]`。 + keep=" " + for slot in $PUBLISH_SLOTS; do keep+="${slot//-/_} "; done + for f in dist/*; do + base=${f##*/} + [ "$base" = "SHA256SUMS.txt" ] && continue + platform_arch=${base%.exe}; platform_arch=${platform_arch%.tar.gz}; platform_arch=${platform_arch#*_*_} + case "$keep" in + *" $platform_arch "*) + echo "publish: $base" + ;; + *) + echo "drop: $base($platform_arch 未在真机验证,不进 Release;仍留在 dist- artifact 里取证)" + rm -f "$f" + ;; + esac + done + + - name: 发布白名单齐全性 + 重算全量 SHA256SUMS shell: bash run: | set -euo pipefail ls -l dist - # 六槽位全齐才发版:缺任意一个就 fail(宁可不出 Release,也不出少一档的 Release)。 - # 每个槽位都要 GUI 与 CLI 两个资产——只按「槽位名出现过」判会被其中一个掩盖。 - for slot in windows-x64 windows-arm64 linux-x64 linux-arm64 macos-arm64 macos-x64; do + # 白名单内槽位必须**全齐**才发版(宁可不出 Release,也不出少一档的 Release)。每个槽位都要 + # GUI 与 CLI 两个资产——只按「槽位名出现过」判会被其中一个掩盖。档位比对用下划线形态: + # 资产名自 D49 起是 `unlucky-launcher__windows_x64.exe`,旧写法 `dist/$prefix-*-$slot*` + # (连字符)对现行命名永远匹配不上,已一并修正。 + for slot in $PUBLISH_SLOTS; do + stem=${slot//-/_} for prefix in unlucky-launcher ull; do - if ! compgen -G "dist/$prefix-*-$slot*" >/dev/null; then - echo "::error::矩阵缺产物:$prefix / $slot" + if ! compgen -G "dist/${prefix}_*_${stem}*" >/dev/null; then + echo "::error::发布白名单缺产物:$prefix / $slot" exit 1 fi done done - # 各平台任务各写一份 SHA256SUMS.txt,合并后只有一份是真的 —— 在 Release 侧重算。 - # GUI 与 CLI 都要进清单:漏了 ull 就是「发了但没有校验和」。 + # 各槽位各写一份 SHA256SUMS.txt,合并后只有一份是真的 —— 在 Release 侧按已发布的资产重算。 cd dist rm -f SHA256SUMS.txt sha256sum unlucky-launcher_* ull_* > SHA256SUMS.txt diff --git a/Cargo.lock b/Cargo.lock index fd7c9e4..fb854ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4982,7 +4982,7 @@ checksum = "ca718471876992059f251e33aafc334346ada08042b7661dd1fe313a9bced557" [[package]] name = "unlucky-agent" -version = "0.2.0" +version = "0.2.1" dependencies = [ "axum", "futures-util", @@ -4997,7 +4997,7 @@ dependencies = [ [[package]] name = "unlucky-app-iced" -version = "0.2.0" +version = "0.2.1" dependencies = [ "iced", "image", @@ -5010,7 +5010,7 @@ dependencies = [ [[package]] name = "unlucky-cli" -version = "0.2.0" +version = "0.2.1" dependencies = [ "serde", "serde_json", @@ -5021,7 +5021,7 @@ dependencies = [ [[package]] name = "unlucky-core" -version = "0.2.0" +version = "0.2.1" dependencies = [ "flate2", "futures-util", @@ -5040,15 +5040,15 @@ dependencies = [ [[package]] name = "unlucky-design" -version = "0.2.0" +version = "0.2.1" [[package]] name = "unlucky-i18n" -version = "0.2.0" +version = "0.2.1" [[package]] name = "unlucky-palette-extract" -version = "0.2.0" +version = "0.2.1" dependencies = [ "image", "unlucky-design", @@ -5056,7 +5056,7 @@ dependencies = [ [[package]] name = "unlucky-presenter" -version = "0.2.0" +version = "0.2.1" dependencies = [ "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index cac3151..ec5926e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ members = [ exclude = ["crates/app"] [workspace.package] -version = "0.2.0" +version = "0.2.1" edition = "2021" license = "Apache-2.0" authors = ["AskaEth "] diff --git a/crates/agent/Cargo.toml b/crates/agent/Cargo.toml index 72e1520..d880766 100644 --- a/crates/agent/Cargo.toml +++ b/crates/agent/Cargo.toml @@ -13,7 +13,7 @@ publish.workspace = true # 端口层(src/port*.rs)让 agent 能在没有 core 实现时独立编译与测试;装配层用 core 实现 # 填充 `Ports`(GUI 侧是 `crates/app`,headless 侧是 `crates/cli` 的 `ull agent serve`)。 [dependencies] -unlucky-core = { path = "../core", version = "0.2" } +unlucky-core = { path = "../core", version = "0.2.1" } # 序列化:工具 JSON Schema 是手写 serde_json::Value(不引 schemars), # 工具结果 / 审计记录 / 事件流都用 serde derive。 diff --git a/crates/app-iced/Cargo.toml b/crates/app-iced/Cargo.toml index 092f6e0..51fb282 100644 --- a/crates/app-iced/Cargo.toml +++ b/crates/app-iced/Cargo.toml @@ -22,12 +22,12 @@ path = "src/lib.rs" [dependencies] # 令牌唯一真源(M61):颜色/排版/形状/间距/图标几何全部只从这里取。 -unlucky-design = { path = "../design", version = "0.2" } +unlucky-design = { path = "../design", version = "0.2.1" } # M85:UI 框架无关的宿主层(DTO / 状态 reducer / host)。**只登记依赖,本 mission 不接线**—— # 页面用它的时机由后续 iced 页面 mission 决定(接线流程见 crates/presenter/src/lib.rs 的 crate 文档)。 # 与 Slint 版共用同一套 host 与状态语义,这是 D26「重写只换渲染」的落点。 -unlucky-presenter = { path = "../presenter", version = "0.2" } +unlucky-presenter = { path = "../presenter", version = "0.2.1" } # UI 框架(D26 锁定 iced 0.14)。**不得依赖 slint**。 # 关掉默认 features 后必须显式给执行器(M60 踩坑 Q2:否则 `No futures executor has been enabled`), @@ -57,11 +57,11 @@ image = { version = "0.25", default-features = false, features = [ # 进 UI 之前分发,命中就把控制权交给 `unlucky_cli::run`。依赖方向 app-iced → cli → # core/agent 不违反 core 零 UI 铁律(core 的依赖树里没有 cli)。零 UI 依赖的 `ull` # 仍在 `crates/cli`,给没有图形库的机器用——两者不是重复品。 -unlucky-cli = { path = "../cli", version = "0.2" } +unlucky-cli = { path = "../cli", version = "0.2.1" } # M92(D28):国际化地基(语言探测 / 消息目录 / 中文字体策略)。零依赖 crate——它拿不到 iced, # 所以系统字体扫描留在这里(`src/fonts/cjk.rs`),本 crate 只把族名喂给它的纯函数。 -unlucky-i18n = { path = "../i18n", version = "0.2" } +unlucky-i18n = { path = "../i18n", version = "0.2.1" } # M97:判据表第 8 行的取证通道。`iced_wgpu` 在真创建设备时会 `log::info!("Selected: {adapter:#?}")`, # 但进程里从来没人装 logger,于是「看不见」被当成了「没发生」。这里装一个只放行 `iced_wgpu*` / diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 81b016e..d58f3c2 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -22,10 +22,10 @@ path = "src/lib.rs" # 参数解析手写(不引 clap):子命令集小,没必要为它多引一棵依赖树(Win10+ 单基线, # 但 CLI 是 headless/服务器场景的产物,体积与启动开销仍是关注项)。 [dependencies] -unlucky-core = { path = "../core", version = "0.2" } +unlucky-core = { path = "../core", version = "0.2.1" } # agent:`redact`(输出脱敏)+ `ull agent serve`(M39 的 MCP server:注册表 → rmcp 双投影, # core 侧五个端口在 src/commands/agent/ports/ 装配)。 -unlucky-agent = { path = "../agent", version = "0.2" } +unlucky-agent = { path = "../agent", version = "0.2.1" } # `ull agent serve` 要长驻跑 rmcp 的 stdio / Streamable HTTP 传输(tokio 运行时)。 tokio.workspace = true serde.workspace = true diff --git a/crates/palette-extract/Cargo.toml b/crates/palette-extract/Cargo.toml index 1177c1f..e074e22 100644 --- a/crates/palette-extract/Cargo.toml +++ b/crates/palette-extract/Cargo.toml @@ -12,7 +12,7 @@ publish.workspace = true # 分层纪律(D40):`crates/design` 保持零第三方依赖,**图片解码只能在这里**。 # 本 crate 的职责边界很窄:解码 + 限尺寸 + 把像素交给 design,不碰 UI、不碰设置、不碰壁纸探测。 [dependencies] -unlucky-design = { path = "../design", version = "0.2" } +unlucky-design = { path = "../design", version = "0.2.1" } # 只开两个编解码器(MIT OR Apache-2.0,deny.toml 白名单内): # - png:启动器背景图最常见的无损格式,也是截图通道用的格式; diff --git a/crates/presenter/Cargo.toml b/crates/presenter/Cargo.toml index 38160c4..dfd7143 100644 --- a/crates/presenter/Cargo.toml +++ b/crates/presenter/Cargo.toml @@ -22,7 +22,7 @@ serde_json.workspace = true # 边界(01c §5 / D3):presenter 只经 DTO 消费 core,**不 import core 的领域类型到 DTO 里**。 # host 是 core 的快照/事件流与本 crate DTO 之间的唯一翻译层,所以它需要 core。 -unlucky-core = { path = "../core", version = "0.2" } +unlucky-core = { path = "../core", version = "0.2.1" } [lints] workspace = true diff --git a/packaging/README.md b/packaging/README.md index cafdd9e..d5aae89 100644 --- a/packaging/README.md +++ b/packaging/README.md @@ -6,16 +6,16 @@ ## 1. 产物矩阵 -| 槽位 | 产物文件名 | 形态 | 归档内容 | -|------|-----------|------|---------| -| `windows-x64` | `unlucky-launcher__windows_x64.exe` | **裸 exe**(双击即用) | exe 单文件;三份合规文本作为并列 Release 资产 | -| `windows-arm64` | `unlucky-launcher__windows_arm64.exe` | **裸 exe**(同 x64 策略) | 同上;在 x64 宿主上交叉编译到 `aarch64-pc-windows-msvc` | -| `linux-x64` | `unlucky-launcher__linux_x64.tar.gz` | 单二进制归档 | `unlucky-launcher` + `LICENSE` + `NOTICE` + `README.md` | -| `linux-arm64` | `unlucky-launcher__linux_arm64.tar.gz` | 单二进制归档(原生 ARM64 runner 构建) | 同上 | -| `macos-arm64` | `unlucky-launcher__macos_arm64.tar.gz` | 单二进制归档 | 同上 | -| `macos-x64` | `unlucky-launcher__macos_x64.tar.gz` | 单二进制归档 | 同上 | -| 每槽位 | `ull__<平台>_<架构>[.exe]` | **裸 CLI 文件**(独立资产,不进 tar.gz) | 零 UI 依赖的 `ull` 本体,进同一份 `SHA256SUMS.txt` | -| 全平台 | `SHA256SUMS.txt` | 校验清单 | 每行 `␠␠<文件名>`(`sha256sum -c` 可直吃) | +| 槽位 | 本版发布 | 产物文件名 | 形态 | 归档内容 | +|------|---------|-----------|------|---------| +| `windows-x64` | ✅ 发布 | `unlucky-launcher__windows_x64.exe` | **裸 exe**(双击即用) | exe 单文件;三份合规文本作为并列 Release 资产 | +| `windows-arm64` | ✅ 发布 | `unlucky-launcher__windows_arm64.exe` | **裸 exe**(同 x64 策略) | 同上;在 x64 宿主上交叉编译到 `aarch64-pc-windows-msvc` | +| `linux-x64` | ❌ 只构建取证 | `unlucky-launcher__linux_x64.tar.gz` | 单二进制归档 | `unlucky-launcher` + `LICENSE` + `NOTICE` + `README.md` | +| `linux-arm64` | ❌ 只构建取证 | `unlucky-launcher__linux_arm64.tar.gz` | 单二进制归档(原生 ARM64 runner 构建) | 同上 | +| `macos-arm64` | ❌ 只构建取证 | `unlucky-launcher__macos_arm64.tar.gz` | 单二进制归档 | 同上 | +| `macos-x64` | ❌ 只构建取证 | `unlucky-launcher__macos_x64.tar.gz` | 单二进制归档 | 同上 | +| 每槽位 | 随所属槽位 | `ull__<平台>_<架构>[.exe]` | **裸 CLI 文件**(独立资产,不进 tar.gz) | 零 UI 依赖的 `ull` 本体,进同一份 `SHA256SUMS.txt` | +| 全平台 | 仅已发布资产 | `SHA256SUMS.txt` | 校验清单 | 每行 `␠␠<文件名>`(`sha256sum -c` 可直吃) | 约定细节: @@ -27,7 +27,7 @@ LICENSE/NOTICE/README.md 是许可义务,而这三份本来就作为并列 Release 资产分发,裸 `ull` 不缺条款。 - `unlucky-launcher` 本体也认全部 CLI 子命令(M82),`ull` 不是第二个界面,而是**不链接图形栈**的同一套命令(差异见 §4 与 §7.4),两者不是重复品。 M118:GUI 的**构建产物**名是 `unlucky-launcher-iced`(`crates/app` 已被 workspace `exclude`,不再产出 `unlucky-launcher`);对外文件名仍是 `unlucky-launcher`,改名在 `make_artifacts.py` 落盘时(`LAUNCHER_BIN` 找产物 / `LAUNCHER_ASSET` 定对外名),所以下载链接 / `SHA256SUMS` / 本文矩阵不变。 -- `` 不带前导 `v`(tag 是 `v0.2.0`,文件名是 `0.2.0`)——由脚本统一裁剪。 +- `` 不带前导 `v`(tag 是 `v0.2.1`,文件名是 `0.2.1`)——由脚本统一裁剪。 - UNIX 归档内 `mtime`/`uid`/`gid` 归零、权限固定(二进制 0755、文本 0644), 同源构建的 sha256 不受打包时刻影响。 - **架构不靠猜**:脚本打包前回读二进制的可执行文件头(PE 机器码 / ELF `e_machine` / @@ -41,7 +41,7 @@ - `` 由 `make_artifacts.py` 通过 `cargo metadata --no-deps` 读出(不是正则扒文件), 任何一个 workspace member 版本不一致时直接失败。 - 带 tag 构建时(CI)额外传 `--version "$TAG"`:脚本裁剪 `v` 后与 `Cargo.toml` 交叉校验, - **不一致即失败**——避免出现 tag 说 0.2.0、产物里写 0.1.0 的发布事故。 + **不一致即失败**——避免出现 tag 说 0.2.1、产物里写 0.2.0 的发布事故。 - `ull --version` 必报同一版本(脚本打包前跑一次冒烟 `ull --version` 断言版本串在其中), 这是 `env!("CARGO_PKG_VERSION")` 的实际输出,不是估算。 - 发布流程:先改 `Cargo.toml` 版本 + 提交 → `git tag v` → 推送 tag 触发流水线。 @@ -54,7 +54,7 @@ cargo build --release --locked # 产出 target/release/{unlucky-launcher-iced,ull}(M118:Slint 版不再进 workspace) cargo build --profile fast --bins # 只是想快点看到画面/跑测试:快档(§6),产物在 target/fast/ python packaging/make_artifacts.py --out dist # 产物落在 dist/,含 SHA256SUMS.txt -python packaging/make_artifacts.py --version v0.2.0 # 顺带校验版本一致性 +python packaging/make_artifacts.py --version v0.2.1 # 顺带校验版本一致性 python packaging/make_artifacts.py --no-smoke # 跳过 ull --version 冒烟 ``` @@ -83,7 +83,7 @@ CI 真机上:`windows-arm64` 由 cross 构建产物 + M50 的 dumpbin 导入 - 触发:`push` tag `v*`;`workflow_dispatch` 只构建不发布(验证矩阵仍编得过)。 `inputs.prerelease` **只在 tag ref 上 dispatch 才可达**——Actions 页面的 "Run workflow" 只能选分支, - 要跑 tag 得 `gh workflow run release.yml --ref v0.2.0`。分支 ref 的 dispatch 与 push 一样被 + 要跑 tag 得 `gh workflow run release.yml --ref v0.2.1`。分支 ref 的 dispatch 与 push 一样被 `release` job 的 `if: github.ref_type == 'tag'` 挡在发布之外,所以「dispatch 只构建不发布」 与「dispatch 能关 pre-release」是两条互不冲突的规则。 - 矩阵(六槽位;`matrix.build_dir` 参数化产物根,脚本不再假设产物一定在 `target/release`): @@ -95,7 +95,12 @@ CI 真机上:`windows-arm64` 由 cross 构建产物 + M50 的 dumpbin 导入 ——查 VC 的 `lib/arm64/libcmt.lib`(`+crt-static` 靠它)与 SDK 的 `um/arm64/kernel32.lib`, 缺了直接给出"给 VS 加 `VC.Tools.ARM64` 或改用原生 `windows-11-arm`"的可执行提示)→ `make_artifacts.py --slot … --build-dir …` → 归档内容自检 → 上传 artifact。 -- `release` job:下载全部 artifact → 重算一份全量 `SHA256SUMS.txt` → 用 `packaging/release-notes.md` +- **发布白名单(`PUBLISH_SLOTS`,唯一开关)**:六槽位一律构建取证,只有白名单里的槽位进 Release 与 + `SHA256SUMS.txt`——当前 = `windows-x64 windows-arm64`;Linux x64/arm64、macOS arm64/x64 四档**从未真构建 + 成功过一次、也没在真机跑过**,不推给用户(产物留在本次运行的 `dist-` artifact 里取证,它们红了也 + 不连坐 Windows 发布)。手动入口 `workflow_dispatch`(分支 ref)把六槽位整体编一遍、不发布;将来某槽位 + 真机验证通过,把它的键加进这一行即打开(齐全性检查与 SHA256SUMS 自动跟着扩),并同步 §1/§9 与本文件。 +- `release` job:下载全部 artifact → **按白名单裁剪** → 重算 `SHA256SUMS.txt` → 用 `packaging/release-notes.md` 模板渲染支持矩阵 → `gh release create`(**pre-release 默认开**:首建时 `push` 触发按 pre-release,`workflow_dispatch` 可关)。 - **同一 tag 重跑幂等**:`gh release view` 命中已存在的 Release 时改走 `gh release upload --clobber` + `gh release edit`,覆盖资产与说明而不是再 `create`(`create` 遇已存在会失败,把流水线最后一步染红)。 @@ -103,7 +108,7 @@ CI 真机上:`windows-arm64` 由 cross 构建产物 + M50 的 dumpbin 导入 `inputs.prerelease` 为空)时**不传** `--prerelease`,所以重跑**不会**翻转标记;只有 tag ref 上的 `workflow_dispatch` 显式给了输入才断言——`prerelease=true` → `--prerelease`,`prerelease=false` → `--prerelease=false`。要改标记两条路等价且互不打架:① 走流水线 - `gh workflow run release.yml --ref v0.2.0 -f prerelease=false`(会落到 edit 分支显式断言) + `gh workflow run release.yml --ref v0.2.1 -f prerelease=false`(会落到 edit 分支显式断言) ② 直接 `gh release edit --prerelease=false`(不经流水线;后续默认重跑不碰该标记, 不会被翻回去)。首建(`create`)仍保持「pre-release 默认开」的老契约,空输入按 `true` 处理。 @@ -220,7 +225,7 @@ macOS 产物是**裸二进制**而非 `.app` bundle:符合单文件承诺, 构建环境:rustc 1.99.1 stable / `x86_64-pc-windows-gnu`(MinGW 链接器)。§7.2/§7.3 的早数字是另有 worker 并发做全量 debug 构建时测的(偏悲观);§7.1 的三行 M96 数据是空载冷编译。 -**本节以下所有输出里的 `0.1.0` 都是当时那次运行的真实输出(workspace 版本那时是 `0.1.0`),属实测记录,不随后续版本号改动而回填**;当前版本见 `Cargo.toml` 的 `[workspace.package] version`。§2 的校验机制不依赖任何写死的数字,改版本号不会让它失效。产物名同理且更容易被误读成现状:§7.3 / §7.5 里的 `unlucky-launcher-0.1.0-windows-*.exe`(**连字符**)是 2026-11-01 那几次实测的原文,D49(2026-11-05)才改成下划线形态——同一槽位现在叫 `unlucky-launcher_0.2.0_windows_x64.exe`(规则见 §1);拿旧名去 Release 页或磁盘上找文件必然找不到,那不代表发布挂了。 +**本节以下所有输出里的 `0.1.0` 都是当时那次运行的真实输出(workspace 版本那时是 `0.1.0`),属实测记录,不随后续版本号改动而回填**;当前版本见 `Cargo.toml` 的 `[workspace.package] version`。§2 的校验机制不依赖任何写死的数字,改版本号不会让它失效。产物名同理且更容易被误读成现状:§7.3 / §7.5 里的 `unlucky-launcher-0.1.0-windows-*.exe`(**连字符**)是 2026-11-01 那几次实测的原文,D49(2026-11-05)才改成下划线形态——同一槽位现在叫 `unlucky-launcher_0.2.1_windows_x64.exe`(规则见 §1);拿旧名去 Release 页或磁盘上找文件必然找不到,那不代表发布挂了。 ### 7.1 体积与冷构建耗时(实测字节数,不是估算) @@ -325,24 +330,23 @@ runner 上的真跑(本机连 `raw.githubusercontent.com` 都解析不了, ## 9. 支持矩阵(对外口径,与 release-notes 模板逐行一致) -| 平台 | 级别 | 运行时要求 | -|------|------|-----------| -| Windows 10/11 x64 | 一等 | 无(UCRT 系统自带) | -| Windows 11 ARM64 | 一等(启动器产物) | 无额外库;启动器原生运行,游戏本体走系统 x64 模拟层 + x64 JDK | -| macOS 12+(arm64 / x64) | 一等 | 无第三方库;未签名需去 quarantine | -| Linux x64 X11 | 一等 | glibc 2.35+、xkbcommon、fontconfig、libGL、xcb | -| Linux x64 Wayland | 支持 | 上述 + wayland-client | -| Linux arm64 X11 | 一等 | glibc 2.35+(`ubuntu-22.04-arm` 原生构建,**与 x64 同地板**)、xkbcommon、fontconfig、libGL、xcb | -| Linux arm64 Wayland | 支持 | 上述 + wayland-client | -| Windows 8.1/8/7 | 不支持 | 基线已上调(D21) | -| Android Termux + X11 | 试验级(不承诺) | 仅 CLI(源码自建),GUI 未验证,不在本发布矩阵内 | +| 平台 | 本版发布 | 级别 | 运行时要求 | +|------|---------|------|-----------| +| Windows 10/11 x64 | ✅ | 一等 | 无(UCRT 系统自带) | +| Windows 11 ARM64 | ✅ | 一等(启动器产物) | 无额外库;启动器原生运行,游戏本体走系统 x64 模拟层 + x64 JDK | +| macOS 12+(arm64 / x64) | ❌ 只构建取证 | 一等(未真机验证) | 无第三方库;未签名需去 quarantine | +| Linux x64 X11 | ❌ 只构建取证 | 一等(未真机验证) | glibc 2.35+、xkbcommon、fontconfig、libGL、xcb | +| Linux x64 Wayland | ❌ 只构建取证 | 支持 | 上述 + wayland-client | +| Linux arm64 X11 | ❌ 只构建取证 | 一等(未真机验证) | glibc 2.35+(`ubuntu-22.04-arm` 原生构建,**与 x64 同地板**)、xkbcommon、fontconfig、libGL、xcb | +| Linux arm64 Wayland | ❌ 只构建取证 | 支持 | 上述 + wayland-client | +| Windows 8.1/8/7 | — | 不支持 | 基线已上调(D21) | +| Android Termux + X11 | — | 试验级(不承诺) | 仅 CLI(源码自建),GUI 未验证,不在本发布矩阵内 | -`packaging/release-notes.md` 的 "Support matrix" 表与上表逐行对应(英文版,Termux 行同样标注 -experimental / not part of this release matrix)——改一边必须同步另一边,否则标题里的"一致"就是假话。 +`packaging/release-notes.md` 的 "Support matrix" 表与上表逐行对应(英文版同样带 published / not +published 一列,Termux 行标注 experimental)——改一边必须同步另一边,否则标题里的"一致"就是假话。 -两个 arm64 槽位都是**正式发布槽位**(产物进 Release、进 `SHA256SUMS.txt`、release job 的六槽位齐全性 -检查会拦下缺失),不要跟 Termux 那行混为一谈:Termux 是"源码自建的实验级旁路",Linux arm64 是 -"原生 runner 编、原生跑"的一等产物。 +两个 arm64 槽位都是**正式矩阵槽位**(不是 Termux 那种源码自建旁路),但**本版只有 `windows-arm64` +发布**:`linux-arm64` 由原生 runner 编、尚未真机验证,发布开关就是 `release.yml` 的 `PUBLISH_SLOTS`。 ## 10. 已知边界 / 后续 @@ -354,28 +358,25 @@ experimental / not part of this release matrix)——改一边必须同步另 4. **Linux musl 静态版**:可彻底摆脱 glibc 地板,但现役 iced 版(winit + wgpu)在 musl 上的可行性未验证,未纳入本矩阵。 5. **`ull` 已随包**(M82 起):每个槽位与 GUI 并列发一份裸 `ull`,进同一份 `SHA256SUMS.txt`,矩阵 完整性检查按「GUI 与 CLI 各自都要有」分开查(`release.yml`)。等 CI 首跑确认这条在真流水线上有效。 -6. **本机 GNU 链接器的已知无害告警**:`cargo build --release`(GNU 工具链)会打一行 - `warning: linker stderr: corrupt .drectve at end of def file`,来自 MinGW `ld` 对 `.drectve` 段的 - 老式抱怨,不影响产物(体积/导入表/冒烟均已实测正常);CI 的 MSVC 槽位不会出现这条。 -7. **两个 arm64 槽位本机跑不了**:本机是 x64 Windows,既没有 ARM 机器也没有 MSVC 的 ARM64 链接库 - 组合,`windows-arm64` / `linux-arm64` 的**真实产物只能由 CI 出**:前者靠 `dumpbin -dependents` - 的静态 CRT 门禁 + 架构校验,后者在 `ubuntu-22.04-arm` 上真跑 `ull --version` 冒烟。 - 本机能做且已做的是脚本层取证(见 §7.5):宿主判定注入(`win32/ARM64`、`linux/aarch64` 等全槽位)、 - 可执行文件头架构识别(真 x64 exe + 合成 PE/ELF/Mach-O 头)、以及把真 exe 的 PE 机器码改成 ARM64 - 当 stub 走完命名/校验/归档全流程 —— 不假装能在 x64 上执行 arm64 二进制。 +6. **本机 GNU 链接器的已知无害告警**:`cargo build --release`(GNU 工具链)会打一行 `warning: linker + stderr: corrupt .drectve at end of def file`(MinGW `ld` 的老式抱怨,不影响产物;CI 的 MSVC 槽位没有)。 +7. **两个 arm64 槽位本机跑不了**:本机是 x64 Windows,既无 ARM 机器也无 MSVC 的 ARM64 链接库组合, + `windows-arm64` / `linux-arm64` 的**真实产物只能由 CI 出**(前者靠 `dumpbin -dependents` 的静态 CRT + 门禁 + 架构校验,后者在 `ubuntu-22.04-arm` 上真跑 `ull --version` 冒烟);本机能做且已做的是脚本层 + 取证(§7.5:宿主判定注入、可执行文件头识别、把真 exe 的 PE 机器码改成 ARM64 当 stub 走完全流程)。 ## 11. CI 运行时冒烟(Linux 真渲染一帧) **细节与依据在 [`packaging/ci-runtime-smoke.md`](ci-runtime-smoke.md)**(本节再增长也往那里写,两个文件都必须 ≤400 行)。 -`ci.yml` 的 `runtime-smoke` job 在 `ubuntu-latest` 上补第一份**真实图形环境**运行证据:装 `xvfb` + 软渲染 -依赖 + 字体,用 `xvfb-run` 起 GUI 进程,走既有进程内截图通道渲出一帧 PNG,再对**像素本身**断言 -(退出码 0 / PNG 尺寸 / 非空白三项,artifact `runtime-smoke-screenshot`)。 +`ci.yml` 的 `runtime-smoke` job 在 `ubuntu-latest` 上补第一份**真实图形环境**证据:装 `xvfb` + 软渲染依赖 + +字体,用 `xvfb-run` 起 GUI 进程渲出一帧 PNG,再对**像素本身**断言(退出码 0 / 尺寸 / 非空白,artifact +`runtime-smoke-screenshot`)。 要点三条: -- 只看退出码不够:PNG 是进程自己编码写出的,「合法 PNG + 退出码 0」不等于画面有内容,像素断言是纵深防御; - 而「完全没有字体」与「非软渲染」这两种失效都是**硬失败**(panic / 报错,不产生 PNG;review 实测),运行步就红。 +- 只看退出码不够:「合法 PNG + 退出码 0」不等于画面有内容;「完全没字体」与「非软渲染」都是**硬失败**(panic / + 报错,不产生 PNG;review 实测)。 - 闸门下限 ≈「至少画出一条标题栏」(review 实测切片仍 pass):证明「场景被真实渲染过」,**不证明界面元素齐全, 更不证明视觉正确**——视觉靠人眼看 artifact。 - 只覆盖 Linux x64;macOS 是 dispatch-only 探针(无实测证据),Windows 已有本机人眼验收。 diff --git a/packaging/release-notes.md b/packaging/release-notes.md index 4ee7559..7a5a684 100644 --- a/packaging/release-notes.md +++ b/packaging/release-notes.md @@ -13,21 +13,24 @@ Asset names are `___`, underscore separated — t | --- | --- | --- | | Windows 10/11 x64 | `unlucky-launcher_{{VERSION}}_windows_x64.exe` | single exe, double-click to run | | Windows 11 ARM64 | `unlucky-launcher_{{VERSION}}_windows_arm64.exe` | single exe, runs natively on ARM64 Windows | -| Linux x64 (X11/Wayland) | `unlucky-launcher_{{VERSION}}_linux_x64.tar.gz` | single binary inside, no installer | -| Linux arm64 (X11/Wayland) | `unlucky-launcher_{{VERSION}}_linux_arm64.tar.gz` | single binary inside; needs glibc 2.35+ | -| macOS 12+ Apple Silicon | `unlucky-launcher_{{VERSION}}_macos_arm64.tar.gz` | single binary inside, no installer | -| macOS 12+ Intel | `unlucky-launcher_{{VERSION}}_macos_x64.tar.gz` | single binary inside, no installer | -Each archive unpacks to one top-level directory of the same name holding the binary plus `LICENSE`, -`NOTICE` and `README.md`; the Windows assets are bare `.exe` files, so those three documents are attached -to this release next to them — the license terms travel with every download. +**This release ships Windows only.** The Linux (x64 / arm64) and macOS (Apple Silicon / Intel) slots are +built by CI but are not assets of this release: they have not been verified on real machines yet, so +nothing unverified is handed out. They stay out of the download list and out of `SHA256SUMS.txt` until +that verification happens — the support matrix below marks them accordingly. -Every slot also ships the command line as its own small bare asset, +The Windows assets are bare `.exe` files, so `LICENSE`, `NOTICE` and `README.md` are attached to this +release next to them: the license terms travel with every download. Slots that ship an archive (Linux / +macOS, once published) unpack to one top-level directory of the same name holding the binary plus those +three documents. + +## Command line + +Each published slot also ships the command line as its own small bare asset, `ull_{{VERSION}}__` (`.exe` on Windows): -```bash -chmod +x ull_{{VERSION}}_linux_x64 # release assets carry no executable bit -./ull_{{VERSION}}_linux_x64 instances list # servers and CI: no graphics stack needed +```powershell +.\ull_{{VERSION}}_windows_x64.exe instances list # servers and CI: no graphics stack needed ``` `ull` runs `instances`, `install`, `pack import`, `launch`, `accounts`, `sessions`, `logs`, `diag`, @@ -36,37 +39,36 @@ before it ever opens a window, so a desktop machine does not need `ull`. ## Support matrix -| Platform | Level | Runtime requirements | -| --- | --- | --- | -| Windows 10/11 x64 | first class | nothing beyond the OS (UCRT is part of Win10+) | -| Windows 11 ARM64 | first class (launcher artifact) | nothing extra; the launcher runs natively, the game itself runs through the OS x64 emulation layer with an x64 JDK | -| macOS 12+ (arm64 / x64) | first class | nothing; unsigned build, see the Gatekeeper note | -| Linux x64, X11 | first class | `libxkbcommon`, `fontconfig`, `libGL` (or software renderer), `libxcb` | -| Linux x64, Wayland | supported | `wayland-client`, `libxkbcommon`, plus the X11 set for XWayland apps | -| Linux arm64, X11 | first class | glibc 2.35+ (same floor as x64), `libxkbcommon`, `fontconfig`, `libGL`, `libxcb` | -| Linux arm64, Wayland | supported | the above plus `wayland-client` | -| Windows 8.1/8/7 | unsupported | the baseline is Windows 10 | -| Android — Termux + X11 | experimental, not promised | CLI only and only when built from source; GUI unverified; not part of this release matrix | +Published = shipped as a download of **this** release, listed in `SHA256SUMS.txt`. -Both arm64 rows are **formally released slots** (assets ship here and in `SHA256SUMS.txt`), not the -experimental Termux path: Linux arm64 is built and smoke-tested on a native ARM64 runner, Windows ARM64 is -cross-built with the same static-CRT gate as x64. +| Platform | Status in this release | Level | Runtime requirements | +| --- | --- | --- | --- | +| Windows 10/11 x64 | **published** | first class | nothing beyond the OS (UCRT is part of Win10+) | +| Windows 11 ARM64 | **published** | first class (launcher artifact) | nothing extra; the launcher runs natively, the game itself runs through the OS x64 emulation layer with an x64 JDK | +| macOS 12+ Apple Silicon | built by CI, not published | first class (unverified) | nothing; unsigned build, needs the Gatekeeper workaround | +| macOS 12+ Intel | built by CI, not published | first class (unverified) | same as above | +| Linux x64, X11 | built by CI, not published | first class (unverified) | `libxkbcommon`, `fontconfig`, `libGL` (or software renderer), `libxcb` | +| Linux x64, Wayland | built by CI, not published | supported (unverified) | `wayland-client`, `libxkbcommon`, plus the X11 set for XWayland apps | +| Linux arm64, X11 | built by CI, not published | first class (unverified) | glibc 2.35+, `libxkbcommon`, `fontconfig`, `libGL`, `libxcb` | +| Linux arm64, Wayland | built by CI, not published | supported (unverified) | the above plus `wayland-client` | +| Windows 8.1/8/7 | not built | unsupported | the baseline is Windows 10 | +| Android — Termux + X11 | not built (no CI slot) | experimental, not promised | CLI only and only when built from source; GUI unverified | + +Both arm64 rows are formal matrix slots, not the experimental Termux path: Windows ARM64 ships here and +is cross-built with the same static-CRT gate as x64; Linux arm64 builds on a native ARM64 runner and +joins this list once someone has run it on real hardware. The rows above and the matrix table in +`packaging/README.md` §9 are the same list — change one, change the other. ## Install **Windows** — put the `.exe` anywhere and run it (pick `_windows_arm64.exe` on ARM64 Windows, `_windows_x64.exe` everywhere else). The first run creates `.ULL/` and `.minecraft/` next to the exe -(portable is the default data mode); the per-user `%APPDATA%\ULL` plus `%APPDATA%\.minecraft`, or a custom -root, are selectable instead. +(portable is the default data mode); the per-user `%APPDATA%\ULL` plus `%APPDATA%\.minecraft`, or a +custom root, are selectable instead. -**Linux / macOS** - -```bash -tar -xzf unlucky-launcher_{{VERSION}}_linux_x64.tar.gz # _linux_arm64 on aarch64; _macos_arm64 / _macos_x64 on macOS -cd unlucky-launcher_{{VERSION}}_linux_x64 # ditto -xattr -d com.apple.quarantine unlucky-launcher # macOS only: unsigned build, Gatekeeper -chmod +x unlucky-launcher && ./unlucky-launcher -``` +Linux / macOS are not part of this release; their asset names are already fixed by the same rule +(`unlucky-launcher_{{VERSION}}_linux_x64.tar.gz`, `_linux_arm64`, `_macos_arm64`, `_macos_x64`) so the +links will not change shape when those slots open. ## No GPU? Software renderer @@ -75,8 +77,11 @@ adapter is present; `--software-renderer` (or its equivalent `--renderer=cpu`) f ## Verify the download +`SHA256SUMS.txt` lists exactly the assets attached to this release (Windows only), and `sha256sum -c` +checks the ones you actually downloaded: + ```bash -sha256sum -c SHA256SUMS.txt --ignore-missing # Linux +sha256sum -c SHA256SUMS.txt --ignore-missing # Linux / WSL shasum -a 256 -c SHA256SUMS.txt # macOS (sha256sum is not bundled) Get-FileHash .\unlucky-launcher_{{VERSION}}_windows_x64.exe -Algorithm SHA256 # Windows PowerShell ``` @@ -97,8 +102,8 @@ Get-FileHash .\unlucky-launcher_{{VERSION}}_windows_x64.exe -Algorithm SHA256 command line. Offline profiles and authlib-injector servers need no id. - Binaries are not code-signed / notarized yet: Windows SmartScreen and macOS Gatekeeper warn on first launch. The checksums above are the integrity reference for now. -- macOS ships the bare binary (single-file promise), not a `.app` bundle: no Dock icon or Finder - double-click — launch it from a terminal. +- When the macOS slot ships it is the bare binary (single-file promise), not a `.app` bundle: no Dock + icon or Finder double-click — launch it from a terminal. ## License @@ -106,7 +111,5 @@ Apache-2.0, third-party notices in `NOTICE`. Not affiliated with Mojang or Micro ## UI framework in this build -Every asset here is built from the iced UI (`crates/app-iced`, iced 0.14, MIT); no Slint code is linked -into it. The earlier Slint UI (`crates/app`) stays in the repository as historical source only — -excluded from the workspace, not built, in no asset and in no release slot. The Slint Royalty-Free 2.0 -attribution it carried belongs to the `v0.1.0` release and remains where it was published. +Every asset here is built from the iced UI (`crates/app-iced`, iced 0.14, MIT); no other UI framework is +linked into it.