From 84fb01b8a3859338f7d1e868ac22c9fd8bc05eb0 Mon Sep 17 00:00:00 2001 From: "jif2.zhang" Date: Fri, 11 Sep 2026 17:12:58 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=87=AA=E5=8A=A8=E9=83=A8=E7=BD=B2=20?= =?UTF-8?q?+=20android=20=E6=AF=8F=E6=AC=A1=E6=8F=90=E4=BA=A4=E8=A7=A6?= =?UTF-8?q?=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docker-build.yml: main 分支推送 → 构建 OTA 包 → Docker 镜像 → :3002 自动部署 - Dockerfile: node:22-alpine, dist + server + ota-package, healthcheck - android-build.yml: 触发改为 on push (所有分支), 每次提交都构建 - ota-package/README.md 占位, 本地 docker build 不报错 - .gitignore / .dockerignore 调整 --- .dockerignore | 12 ++++ .gitea/workflows/android-build.yml | 4 +- .gitea/workflows/docker-build.yml | 100 +++++++++++++++++++++++++++++ .gitignore | 4 +- Dockerfile | 61 ++++++++++++++++++ ota-package/README.md | 6 ++ 6 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitea/workflows/docker-build.yml create mode 100644 Dockerfile create mode 100644 ota-package/README.md diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a672017 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +# Docker build context 排除项 (镜像内自行构建 dist, 不需要本地构建产物/依赖) +node_modules +dist +.git +.gitea +src-tauri +prototype +photograph + +# 本地 ota-package 中的私钥不得进入镜像 (CI 里的 ota-package 只含公钥产物) +ota-package/hotswap.key +.DS_Store \ No newline at end of file diff --git a/.gitea/workflows/android-build.yml b/.gitea/workflows/android-build.yml index ee130ed..f6bc391 100644 --- a/.gitea/workflows/android-build.yml +++ b/.gitea/workflows/android-build.yml @@ -1,13 +1,11 @@ name: Android Build & Publish -# 仅在 android-* 分支 push 时触发 (如 android/release, android-1.0) +# 每次提交触发 (所有分支 push; 主分支提交会同时触发 docker-build.yml 自动部署) # 构建 release APK (keystore 签名) 并上传到 Cloudflare R2 (S3 兼容 API) # R2 凭据从配置注册中心 (bh.vps.honor3.com:8008, CommonExternalService, 分组 cloudflare) 读取 # 参考: D:\workbench\iboard\.gitea\workflows\android-build.yml on: push: - branches: - - 'android-*' jobs: build-android: diff --git a/.gitea/workflows/docker-build.yml b/.gitea/workflows/docker-build.yml new file mode 100644 index 0000000..363c1a4 --- /dev/null +++ b/.gitea/workflows/docker-build.yml @@ -0,0 +1,100 @@ +name: Docker Build and Push + +# 仅 main 分支 push 触发 (每次提交 → 自动部署) +# 每次主分支提交: 构建前端 + 生成 OTA 升级包 (minisign 签名) → 打进 Docker 镜像 → 部署到 :3002 +# OTA 随镜像发布: 容器内 OTA_PACKAGE_DIR=/app/ota-package 指向打包的 ota-package/ +# 依赖配置注册中心 (bh.vps.honor3.com:8008): +# - service-wardrobe / ota : MINISIGN_PRIVATE_KEY (整段 .key 内容, 多行) +on: + push: + branches: + - main + - master + +jobs: + build-image: + runs-on: ubuntu-latest + permissions: + contents: read + env: + # VITE_API_BASE 留空 = 前端走同源 /api (OTA server 同进程托管首页 + OTA 接口) + VITE_API_BASE: "" + 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' + + steps: + - 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 + # dist/ 供 OTA 打包用 (Dockerfile 内也会构建一份) + run: npm run build + + - name: Install minisign + run: sudo apt-get update && sudo apt-get install -y minisign + + - name: Fetch config center (minisign key) + 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 "" + 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", []) + wanted = {} + for cfg in data: + if cfg.get("key") == "MINISIGN_PRIVATE_KEY": + wanted[cfg["key"]] = cfg.get("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}<