79830cb43f
Deploy Client / build-windows (push) Successful in 1m42s
Deploy Client / build-android (push) Successful in 5m8s
Deploy Client / build-macos (push) Successful in 8m3s
Deploy Client / build-ios (push) Successful in 4m59s
Deploy Client / release-deploy (push) Successful in 2m2s
run 239:android/windows/macos 全绿、ios 在 Checkout 阶段 git fetch git.51yanmei.com 走 frps 超时挂掉(非 iOS 构建问题;run 238 同代码 fetch 碰巧 成功)。因上一版把 build-ios 加进 release-deploy 的 needs,而 gitea 里 continue-on-error 的 job 失败**仍会 skip 下游 needs** → release-deploy 被跳过。 修:needs 保留四平台(等全部完成、不抢跑 → 收齐已上传产物),但加 `if: always() && needs.build-android.result == 'success'` —— 只要 android (稳定 floor)成功即发布,mac/ios/windows 的瞬断失败不再 skip release-deploy, download-artifact 收当时存在的产物照发。 yaml 校验通过。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A79VtQA1BwTuQN1ThpvYpo
234 lines
11 KiB
YAML
234 lines
11 KiB
YAML
name: Deploy Client
|
||
|
||
# Mirrors ~/code/jiu/.gitea/workflows/deploy-client.yml's tag→build→release→
|
||
# deploy shape. Android + Windows are required (release-deploy `needs` them);
|
||
# macOS + iOS (Phase 3, see docs/superpowers/plans/2026-07-05-cicd.md) are
|
||
# intentionally DECOUPLED — see the "why build-macos/build-ios don't block"
|
||
# note above the build-macos job below for the mechanism and rationale.
|
||
#
|
||
# TODO(controller) — RUNNER AVAILABILITY: per docs/ci-runner.md, pangolin
|
||
# currently has exactly ONE registered Gitea Actions runner
|
||
# ("mac-pangolin-2", label `nas:host`). Neither `runs-on: mac` nor
|
||
# `runs-on: windows` below has any runner registered to pick it up yet — this
|
||
# workflow will queue forever until that's fixed. Options: (a) register
|
||
# mac-pangolin-2 with an additional `mac` label (it's already a mac host —
|
||
# cheapest fix for build-android/release-deploy) and separately stand up +
|
||
# register an actual Windows host runner labeled `windows` for build-windows
|
||
# (no such machine exists per docs/ci-runner.md), or (b) repoint both at
|
||
# `nas` and accept that Android/Windows builds then compete with the
|
||
# docker-in-domain nas jobs on the same single mac host. This mirrors the
|
||
# `runs-on: mac` / `runs-on: windows` split already planned in
|
||
# docs/superpowers/plans/2026-07-05-cicd.md Task 7/10 — written that way here
|
||
# for fidelity to that plan, NOT because the runners are confirmed to exist.
|
||
on:
|
||
push:
|
||
tags:
|
||
- 'client-v[0-9]*.[0-9]*.[0-9]*'
|
||
workflow_dispatch:
|
||
|
||
concurrency:
|
||
group: deploy-client
|
||
# true:新 client-v* 取消仍在跑的旧发版(如 windows 机离线导致 build-windows 无限排队
|
||
# 卡住的旧 run),让最新版顺利发布,避免并发组被僵尸 run 占死。
|
||
cancel-in-progress: true
|
||
|
||
jobs:
|
||
build-android:
|
||
runs-on: mac
|
||
env:
|
||
GOPROXY: https://goproxy.cn,direct
|
||
PUB_HOSTED_URL: https://pub.flutter-io.cn
|
||
FLUTTER_STORAGE_BASE_URL: https://storage.flutter-io.cn
|
||
# sing-box 源码经 NAS gitea 公网镜像取(mac runner 连不上 github)。
|
||
# 用稳定公网域名 git.51yanmei.com(ali frps ← 家里 NAS frpc,nginx 443 反代
|
||
# gitea)—— 外网/内网都通,不依赖本地 relay/tailscale。见 scripts/build-libbox.sh。
|
||
SINGBOX_GIT: https://git.51yanmei.com/wangjia/sing-box.git
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Compile (Android APK)
|
||
env:
|
||
RELEASE_KEYSTORE: ${{ secrets.RELEASE_KEYSTORE }}
|
||
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
||
REF_NAME: ${{ gitea.ref_name }}
|
||
run: bash scripts/ci/compile-android.sh "$REF_NAME"
|
||
|
||
- name: Upload android artifact
|
||
uses: actions/upload-artifact@v3
|
||
with:
|
||
name: android
|
||
path: dist/
|
||
|
||
build-windows:
|
||
runs-on: windows
|
||
env:
|
||
GOPROXY: https://goproxy.cn,direct
|
||
PUB_HOSTED_URL: https://pub.flutter-io.cn
|
||
FLUTTER_STORAGE_BASE_URL: https://storage.flutter-io.cn
|
||
# 境内镜像:GitHub release 资产在国内被 GFW 限速 → windows runner(LAN 内)下
|
||
# sing-box.exe / wintun.zip 超时。改从 NAS Gitea generic 包镜像拉,
|
||
# fetch-desktop-bin.sh 命中镜像后照样验 SHA256,失败则回退官方源。
|
||
# ⚠️ 基址含 sing-box 版本目录(v1.13.12)——升级 app/kernel/VERSION 的
|
||
# SINGBOX_VERSION 时,须把新版 zip 重新 PUT 到对应版本目录并同步改这里。
|
||
DESKTOP_BIN_MIRROR: http://192.168.3.200:3000/api/packages/wangjia/generic/desktop-bin/v1.13.12
|
||
# 包默认可匿名读,token 非必需;带上以防将来把包设为私有(未设/为空则匿名 GET)。
|
||
DESKTOP_BIN_MIRROR_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Compile (Windows installer)
|
||
shell: bash
|
||
env:
|
||
REF_NAME: ${{ gitea.ref_name }}
|
||
run: bash scripts/ci/compile-windows.sh "$REF_NAME"
|
||
|
||
- name: Upload windows artifact
|
||
uses: actions/upload-artifact@v3
|
||
with:
|
||
name: windows
|
||
path: dist/
|
||
|
||
# build-macos/build-ios 用 `continue-on-error: true` → 即便 Apple 侧构建抖了
|
||
# (frps 拉 sing-box 源码 / 公证超时等),也不阻塞 android+windows 发布:
|
||
# 1. release-deploy 现在 `needs:` 全部四个平台(见下),所以它会**等**全部
|
||
# build 完成再发布 —— 避免旧 `needs: [build-android]` 只等 android、
|
||
# macos/ios 还没传产物就发布导致漏平台的竞态。
|
||
# 2. continue-on-error 让 macos/ios 失败仍算「completed」满足 needs,不使整个
|
||
# workflow 变红、不阻断已成功平台的发布;release-deploy 的「Download all
|
||
# artifacts」(无 name 过滤)只捡实际存在的产物,缺某平台不报错。
|
||
# 3. Apple 签名 secret 现已在 gitea 用户级配齐(DEVELOPER_ID_P12/IOS_DIST_P12/
|
||
# APPSTORE_API_* 等),macos/ios 正常应成功;continue-on-error 只是抖动兜底。
|
||
build-macos:
|
||
runs-on: mac
|
||
continue-on-error: true
|
||
env:
|
||
GOPROXY: https://goproxy.cn,direct
|
||
PUB_HOSTED_URL: https://pub.flutter-io.cn
|
||
FLUTTER_STORAGE_BASE_URL: https://storage.flutter-io.cn
|
||
# macOS 同样内嵌 libbox → 同走 NAS gitea 公网镜像取 sing-box 源码(见 build-android)。
|
||
SINGBOX_GIT: https://git.51yanmei.com/wangjia/sing-box.git
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Compile (macOS System Extension app)
|
||
env:
|
||
# 左=脚本期望的 env 名(勿改),右=实际密钥名。Apple 证书/公证密钥是
|
||
# 账号级、跨项目唯一 → 放【全局(用户级)密钥】用通用短名,pangolin/jiu 各自
|
||
# 在此映射到自己脚本的 env。Developer ID Application 证书=站外分发,一张签所有 app。
|
||
MACOS_DEVELOPER_ID_CERT_P12_BASE64: ${{ secrets.DEVELOPER_ID_P12 }}
|
||
MACOS_DEVELOPER_ID_CERT_PASSWORD: ${{ secrets.P12_PASSWORD }}
|
||
MACOS_APP_PROVISION_PROFILE_BASE64: ${{ secrets.MACOS_APP_PROVISION_PROFILE_BASE64 }}
|
||
MACOS_SYSEXT_PROVISION_PROFILE_BASE64: ${{ secrets.MACOS_SYSEXT_PROVISION_PROFILE_BASE64 }}
|
||
APPSTORE_API_KEY_ID: ${{ secrets.APPSTORE_API_KEY_ID }}
|
||
APPSTORE_API_ISSUER_ID: ${{ secrets.APPSTORE_API_ISSUER_ID }}
|
||
APPSTORE_API_KEY_P8_BASE64: ${{ secrets.APPSTORE_API_KEY_P8_BASE64 }}
|
||
REF_NAME: ${{ gitea.ref_name }}
|
||
run: bash scripts/ci/compile-macos.sh "$REF_NAME"
|
||
|
||
- name: Upload macos artifact
|
||
uses: actions/upload-artifact@v3
|
||
with:
|
||
name: macos
|
||
path: dist/
|
||
|
||
build-ios:
|
||
runs-on: mac
|
||
continue-on-error: true
|
||
env:
|
||
GOPROXY: https://goproxy.cn,direct
|
||
PUB_HOSTED_URL: https://pub.flutter-io.cn
|
||
FLUTTER_STORAGE_BASE_URL: https://storage.flutter-io.cn
|
||
# iOS 同样内嵌 libbox → 同走 NAS gitea 公网镜像取 sing-box 源码(见 build-android)。
|
||
SINGBOX_GIT: https://git.51yanmei.com/wangjia/sing-box.git
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Compile & upload to TestFlight (iOS)
|
||
env:
|
||
# 证书=账号级 →【全局】通用短名(Apple Distribution 证书,签 iOS App Store/TestFlight);
|
||
# 描述文件=与 bundle id 绑定 →【项目级】pangolin 仓库密钥。
|
||
IOS_DIST_CERT_P12_BASE64: ${{ secrets.IOS_DIST_P12 }}
|
||
IOS_DIST_CERT_PASSWORD: ${{ secrets.IOS_DIST_PASSWORD }}
|
||
IOS_APP_PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_APP_PROVISIONING_PROFILE_BASE64 }}
|
||
IOS_PACKETTUNNEL_PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_PACKETTUNNEL_PROVISIONING_PROFILE_BASE64 }}
|
||
APPSTORE_API_KEY_ID: ${{ secrets.APPSTORE_API_KEY_ID }}
|
||
APPSTORE_API_ISSUER_ID: ${{ secrets.APPSTORE_API_ISSUER_ID }}
|
||
APPSTORE_API_KEY_P8_BASE64: ${{ secrets.APPSTORE_API_KEY_P8_BASE64 }}
|
||
REF_NAME: ${{ gitea.ref_name }}
|
||
run: bash scripts/ci/compile-ios.sh "$REF_NAME"
|
||
# No artifact upload — compile-ios.sh uploads straight to TestFlight via
|
||
# altool (matches jiu); nothing is produced under dist/ for this job.
|
||
|
||
# release-deploy 发布策略:needs 全部四平台(等它们**完成**,不抢跑),但用
|
||
# `if` 让 mac/ios/windows 的失败不 skip 本 job —— 只要 android(稳定 floor)成功即发。
|
||
# 背景:build-macos/ios 必须跑在 mac runner,其 Checkout(git fetch git.51yanmei.com)
|
||
# 走 frps 隧道偶发超时挂掉(run 239 的 ios 就是 fetch fdc13ea 超时,非 iOS 构建问题;
|
||
# run 238 同代码 fetch 碰巧成功)。这类瞬断不该阻断已成功平台的发布。
|
||
# · needs 四平台 → download-artifact 收到当时**全部已上传**的产物(修旧
|
||
# `needs:[build-android]` 只等 android、macos/ios 没传就发布漏平台的竞态)。
|
||
# · continue-on-error 在 gitea 里**不**让下游 needs 在失败时继续(会 skip),故必须
|
||
# 配 `if: always() && needs.build-android.result=='success'` 才能「等全部、失败不阻」。
|
||
release-deploy:
|
||
needs: [build-android, build-windows, build-macos, build-ios]
|
||
if: ${{ always() && needs.build-android.result == 'success' }}
|
||
# ubuntu-latest = 家里 nas act_runner,与 gitea 同机/同网:Release → Forgejo 的
|
||
# API 调用走本地不过 frps 隧道(mac runner 走 git.51yanmei.com→frps→抖,curl 超时
|
||
# exit 28,3m3s=release_ensure 60s×3 重试耗尽)。deploy-server/deploy-site 同为
|
||
# ubuntu-latest,其 Forgejo release 步稳定通过(server 仅 13s)。此 job 全为网络/
|
||
# SSH 步骤(下载产物/传 Forgejo/SSH pangolin1/通知),无 mac 专属需求。
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
|
||
# 一次性下所有 artifact(不带 name),避免同 job 内两次复用 download-artifact
|
||
# action → act 对其只读缓存 git 仓库做二次操作时 EACCES(pack idx 444)。
|
||
- name: Download all artifacts
|
||
uses: actions/download-artifact@v3
|
||
with:
|
||
path: dist-raw/
|
||
|
||
- name: Flatten artifacts into dist/
|
||
shell: bash
|
||
run: |
|
||
mkdir -p dist
|
||
find dist-raw -type f -exec cp {} dist/ \;
|
||
echo "dist/ 内容:"; ls -la dist/
|
||
|
||
- name: Release → Forgejo
|
||
env:
|
||
FORGEJO_TOKEN: ${{ secrets.FORGEJO_TOKEN }}
|
||
FORGEJO_URL: ${{ secrets.FORGEJO_URL }}
|
||
# Needed here (not just in the "Deploy" step below) because
|
||
# release-client.sh now also SSH-pushes the auto-update manifest
|
||
# (version.yaml) straight to pangolin1's /etc/pangolin/ — see the
|
||
# header comment in scripts/ci/release-client.sh.
|
||
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||
REF_NAME: ${{ gitea.ref_name }}
|
||
run: bash scripts/ci/release-client.sh "$REF_NAME"
|
||
|
||
- name: Deploy → pangolin1 (downloads/)
|
||
env:
|
||
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||
REF_NAME: ${{ gitea.ref_name }}
|
||
run: bash scripts/ci/deploy-client.sh "$REF_NAME"
|
||
|
||
- name: Notify
|
||
if: always()
|
||
env:
|
||
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||
REF_NAME: ${{ gitea.ref_name }}
|
||
JOB_STATUS: ${{ job.status }}
|
||
run: |
|
||
. scripts/ci/notify.sh
|
||
if [ "$JOB_STATUS" = "success" ]; then
|
||
notify_ok "client $REF_NAME released + deployed"
|
||
else
|
||
notify_fail "client $REF_NAME pipeline failed"
|
||
fi
|