Files
Unlucky-Launcher/.github/workflows/release-publish.yml
T

178 lines
10 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Release / publish(可复用)
# Release 流水线的**发布侧**(M145 拆分:`release.yml` 顶到 400 行红线)。本文件 = 拆分前 `release.yml` 的 `release`
# job **逐字搬家**:裁剪白名单外的槽位产物、重算全量 `SHA256SUMS.txt`、渲染 `packaging/release-notes.md` 模板、
# `gh release create/edit` 的同 tag 幂等,全部照旧。留在调用方 `release.yml` 的只有三件事:`needs: build`、
# tag 门禁 `if: always() && !cancelled() && github.ref_type == 'tag'`、以及 `permissions: contents: write` 的授予。
# 与拆分前的三处等价改写(都只涉及"从哪里读",不涉及行为):
# ① `secrets.GITHUB_TOKEN` → 本文件声明的入参 secret `PUBLISH_TOKEN`(调用方透传 `secrets.GITHUB_TOKEN`)。
# 可复用工作流**拿不到**调用方未显式传入的 secret,写 `secrets.GITHUB_TOKEN` 会静默为空 → gh 401。
# ② `inputs.prerelease`(原为 `workflow_dispatch` 的输入)→ `github.event.inputs.prerelease`(**字符串**形态的同一个
# 值:`push` 触发时不存在 ⇒ 空串,dispatch 时是 `"true"`/`"false"`),下面 `case "${PRERELEASE:-}"` 的三条分支与
# 拆分前完全一致(空值 = 不碰该标记)。为什么不用 `workflow_call` 入参转一道:dispatch 的 `inputs.prerelease` 是
# **boolean**,而「空值 vs true/false」这条三态语义只有字符串能表达 —— 布尔入参无法取空,硬塞进字符串入参则是
# 类型不符(`uses` 的 `with:` 会按被调方声明的类型校验;actionlint 当场报 `bool value cannot be assigned`)。
# 取 event 上的字符串形态既保住三态,又不需要任何类型转换。
# ③ `PUBLISH_SLOTS` / `runs-on` / `permissions` 的**内容**未动,只是被调方必须自己声明(被调方不继承调用方的
# workflow 级 `env`;权限取调用方与本文件的交集 —— 两边都声明 `contents: write` 才拿得到写权)。
on:
workflow_call:
secrets:
PUBLISH_TOKEN:
description: "调用方透传 `secrets.GITHUB_TOKEN`(本 job 已声明 contents: write,`gh release` 用它发版)"
required: true
# 权限取交集:调用方 release.yml 的 release job 与本文件都声明 contents: write,交集才是 write(不声明则被收窄成 read)。
permissions:
contents: write
jobs:
release:
name: GitHub Release
runs-on: ubuntu-latest
permissions:
contents: write
env:
# ── 发布白名单:唯一决定「哪几个槽位进 Release」的开关(空格分隔,键同 matrix.slot)──
# **M142 起只有 `windows-x64`**:arm64 连试四轮未走通,而下面齐全性检查是「名单内少一档就 fail」——名字挂着
# arm64 就等于 x64 编成功也发不出去。故把它移出名单:它照留在矩阵里跑取证(诊断注解是将来修它的唯一证据),
# **红了不挡发布**。**可逆**:arm64 修好后把键加回这一行即可(齐全性检查与 SHA256SUMS 自动跟着扩),再同步
# README §1/§9 与 release-notes.md 两张表。四档 Linux/macOS 仍未验证,默认连构建都不跑(M139)。
PUBLISH_SLOTS: "windows-x64"
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: 裁掉白名单外的槽位产物(构建取证 ≠ 发布)
shell: bash
run: |
set -euo pipefail
# 槽位键 `windows-x64` 落到文件名是 `windows_x64`(asset_stem),故取文件名里的「平台_架构」两段比白名单。
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 不在发布白名单:四档未验证 / arm64 待修;仍留在 dist-<slot> artifact 里取证)"
rm -f "$f"
;;
esac
done
- name: 发布白名单齐全性 + 重算全量 SHA256SUMS
shell: bash
run: |
set -euo pipefail
ls -l dist
# 白名单内槽位必须**全齐**才发版(宁可不出 Release,也不出少一档的 Release)。每个槽位都要 GUI 与 CLI 两个
# 资产——只按「槽位名出现过」判会被其中一个掩盖;比较用下划线形态(资产名自 D49 起是 `..._windows_x64.exe`)。
# M142 后白名单只剩 x64 一档:arm64 缺产物不再拦发布,但 x64 自己缺 GUI 或 CLI 照样在这里 fail。
for slot in $PUBLISH_SLOTS; do
stem=${slot//-/_}
for prefix in unlucky-launcher ull; do
if ! compgen -G "dist/${prefix}_*_${stem}*" >/dev/null; then
echo "::error::发布白名单缺产物:$prefix / $slot"
exit 1
fi
done
done
# 各槽位各写一份 SHA256SUMS.txt,合并后只有一份是真的 —— 在 Release 侧按已发布的资产重算。
cd dist
rm -f SHA256SUMS.txt
sha256sum unlucky-launcher_* ull_* > SHA256SUMS.txt
sha256sum -c SHA256SUMS.txt
cat SHA256SUMS.txt
- name: 生成更新清单 manifest.json(D50:固定名挂 Release 资产)
shell: bash
env:
TAG: ${{ github.ref_name }}
RELEASE_BASE: ${{ github.server_url }}/${{ github.repository }}/releases/download
run: |
set -euo pipefail
# 先自验再产文件:golden 对拍 + 「DTO 严格解析」镜像(未知字段 / 摘要长度 / size=0 / 版本重复
# 这些在发布步当场炸,而不是变成用户机器上的「查不到更新」)。零第三方依赖,毫秒级。
python3 packaging/make_manifest.py --self-test
# 事实来源是上一步**重算**出来的 dist/SHA256SUMS.txt:清单不另算一份摘要,两者不可能不一致。
# url 钉死 tag(不是 latest):清单里每条都指向与它自报版本相符的字节。
# min_supported / notes 刻意先不写:前者是「收回老版本支持」的发布策略(未拍板,写错会强制升级),
# 后者在 Release 正文里已有,两处各写一份迟早漂。
python3 packaging/make_manifest.py \
--dist dist \
--version "$TAG" \
--channel stable \
--asset-base-url "$RELEASE_BASE/$TAG" \
--released-at "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
- name: 生成 Release 说明(支持矩阵模板)
shell: bash
env:
TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
python3 - <<'PY'
import os, pathlib
version = os.environ["TAG"].lstrip("v")
template = pathlib.Path("packaging/release-notes.md").read_text(encoding="utf-8")
sums = pathlib.Path("dist/SHA256SUMS.txt").read_text(encoding="utf-8").strip()
body = template.replace("{{VERSION}}", version).replace("{{SUMS}}", sums)
pathlib.Path("release-body.md").write_text(body, encoding="utf-8", newline="\n")
print(body[:400])
PY
- name: 创建或更新 GitHub Release(同 tag 重跑幂等)
env:
GH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
TAG: ${{ github.ref_name }}
PRERELEASE: ${{ github.event.inputs.prerelease }}
shell: bash
run: |
set -euo pipefail
base_flags=(--title "Unlucky Launcher $TAG" --notes-file release-body.md)
# pre-release 只在**显式要求**时断言:PRERELEASE=true/false 来自 tag ref 上的 dispatch 输入;空值 = push
# 触发(或它的 Re-run),此时**不碰**该标记(否则会把用户手动提升为 stable 的 Release 静默翻回 pre-release)。
# 首次 create 仍守「pre-release 默认开」老契约:空值按 true 处理。
create_mark=()
edit_mark=()
case "${PRERELEASE:-}" in
true)
create_mark=(--prerelease)
edit_mark=(--prerelease)
;;
false)
edit_mark=(--prerelease=false)
;;
"")
create_mark=(--prerelease)
;;
*)
echo "::error::非预期的 github.event.inputs.prerelease 取值:$PRERELEASE"
exit 1
;;
esac
# Windows 单 exe 装不下 LICENSE/NOTICE/README.md,故三份与产物并列挂 Release(条款随包);`ull` 是独立资产。
# manifest.json 是更新清单(D50):固定文件名,客户端问 `releases/latest/download/manifest.json`。
# D51 定案(2026-11-06,方案甲):GitHub 的 `releases/latest/download/*` 只指向**非
# prerelease** 的最新 Release。稳定版发布时显式 dispatch `prerelease=false`(latest 随之生效,
# 客户端地址不变);push tag 默认仍 prerelease(视为预发布观察,不进稳定通道)。
# 发版 checklist 第六件 = 确认 prerelease 标记与通道一致;v0.2.2 起首个适用。
assets=(dist/unlucky-launcher_* dist/ull_* dist/manifest.json dist/SHA256SUMS.txt LICENSE NOTICE README.md)
if gh release view "$TAG" >/dev/null 2>&1; then
# 同 tag 重跑(补丁/重编):Release 已存在 → 覆盖资产 + 刷新说明,再 `create` 会因已存在而失败。
gh release upload "$TAG" "${assets[@]}" --clobber
gh release edit "$TAG" "${base_flags[@]}" "${edit_mark[@]}"
else
gh release create "$TAG" "${assets[@]}" "${base_flags[@]}" "${create_mark[@]}"
fi