name: OTA Frontend Build & Sign # 手动触发 (发版前用): 构建前端 → 打生产 dist 的 OTA 升级包 (minisign 签名) → 上传 R2 # 产物: # - ota-package/wardrobe-ota.tar.gz (前端替换包) # - ota-package/latest.json (manifest, 客户端 check 用) # 存储: Cloudflare R2 store/ota/ (公开桶) # 依赖配置注册中心 (bh.vps.honor3.com:8008): # - service-wardrobe / ota : MINISIGN_PRIVATE_KEY (整段 .key 内容, 多行) # - CommonExternalService / cloudflare : CF_API_TOKEN, CF_R2_PUBLIC_URL # 注意: 不修改 src-tauri/tauri.conf.json 的 pubkey, 换密钥请手动改 conf 并 commit。 # 部署 server (托管 OTA check 端点 + 首页 dist) 见 README "OTA 升级 与 首页" 章节。 on: workflow_dispatch: inputs: sequence: description: 'OTA sequence (单调递增整数, 留空用 timestamp)' required: false type: string notes: description: '发布说明 (写入 latest.json.notes)' required: false type: string default: '' jobs: build-ota: runs-on: ubuntu-latest permissions: contents: read env: CONFIG_CENTER_URL: '${{ secrets.CONFIG_CENTER_URL }}' CONFIG_CENTER_TOKEN: '${{ secrets.CONFIG_CENTER_TOKEN }}' REGISTRY_URL_FALLBACK: 'http://bh.vps.honor3.com:8008' CFG_PROJECT_SIGNING: 'proj-mtwjnvir-k52zvp' CFG_PROJECT_R2: 'proj-mts0s5jo-fb3rz7' steps: - name: Checkout uses: https://gitea.com/actions/checkout@v4 - name: Setup Node uses: https://gitea.com/actions/setup-node@v4 with: node-version: 22 - name: Install deps run: npm ci --no-audit --no-fund - name: Build frontend # 复用 vite build 产物 (同时是 OTA bundle 内容) run: npm run build - name: Install minisign run: sudo apt-get update && sudo apt-get install -y minisign - name: Fetch config center (minisign key + R2) run: | set -euo pipefail export CONFIG_CENTER_URL="${CONFIG_CENTER_URL:-$REGISTRY_URL_FALLBACK}" python3 - <<'PYEOF' import json, os, urllib.request url = os.environ["CONFIG_CENTER_URL"] token = os.environ.get("CONFIG_CENTER_TOKEN", "") or "" wanted = {} req = urllib.request.Request(f"{url}/api/projects/{os.environ['CFG_PROJECT_SIGNING']}/configs") if token: req.add_header("Authorization", "Bearer " + token) with urllib.request.urlopen(req, timeout=20) as r: data = json.load(r).get("data", []) for cfg in data: if cfg.get("key") in ("MINISIGN_PRIVATE_KEY",): wanted[cfg["key"]] = cfg.get("value", "") req = urllib.request.Request(f"{url}/api/projects/{os.environ['CFG_PROJECT_R2']}/configs") if token: req.add_header("Authorization", "Bearer " + token) with urllib.request.urlopen(req, timeout=20) as r: data = json.load(r).get("data", []) for cfg in data: if cfg.get("key") in ("CF_API_TOKEN", "CF_R2_PUBLIC_URL"): wanted[cfg["key"]] = cfg.get("value", "") # GITHUB_ENV 多行值需用 heredoc 语法; 单行值直接 NAME=value with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as f: for k, v in wanted.items(): if "\n" in v or v.startswith("untrusted comment"): f.write(f"{k}<