Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f6e565ffdc | |||
| ba5f71b716 | |||
| 7c26050cdb | |||
| 673914783d | |||
| 443d4e3a11 |
+12
-11
@@ -21,7 +21,7 @@ jobs:
|
|||||||
# ── Job 1: Lint (shellcheck) ─────────────────────────────────────────────
|
# ── Job 1: Lint (shellcheck) ─────────────────────────────────────────────
|
||||||
lint:
|
lint:
|
||||||
name: Lint — shellcheck
|
name: Lint — shellcheck
|
||||||
runs-on: nas
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -53,12 +53,13 @@ jobs:
|
|||||||
/mnt/scripts/ci/deploy-site.sh \
|
/mnt/scripts/ci/deploy-site.sh \
|
||||||
/mnt/scripts/ci/compile-backend.sh \
|
/mnt/scripts/ci/compile-backend.sh \
|
||||||
/mnt/scripts/ci/release-server.sh \
|
/mnt/scripts/ci/release-server.sh \
|
||||||
/mnt/scripts/ci/deploy-server.sh
|
/mnt/scripts/ci/deploy-server.sh \
|
||||||
|
/mnt/scripts/ci/test.sh
|
||||||
|
|
||||||
# ── Job 2: OpenAPI Sync Check ────────────────────────────────────────────
|
# ── Job 2: OpenAPI Sync Check ────────────────────────────────────────────
|
||||||
openapi-check:
|
openapi-check:
|
||||||
name: OpenAPI Sync Check
|
name: OpenAPI Sync Check
|
||||||
runs-on: nas
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -75,7 +76,7 @@ jobs:
|
|||||||
# ── Job 3: Redline Word Scan (脱敏) ──────────────────────────────────────
|
# ── Job 3: Redline Word Scan (脱敏) ──────────────────────────────────────
|
||||||
redline-scan:
|
redline-scan:
|
||||||
name: Redline Scan — 脱敏 (UI 文案)
|
name: Redline Scan — 脱敏 (UI 文案)
|
||||||
runs-on: nas
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -86,7 +87,7 @@ jobs:
|
|||||||
# ── Job 4: Flutter 客户端(分析 + 单测/组件测试)────────────────────────
|
# ── Job 4: Flutter 客户端(分析 + 单测/组件测试)────────────────────────
|
||||||
flutter-client:
|
flutter-client:
|
||||||
name: Flutter — analyze + test
|
name: Flutter — analyze + test
|
||||||
runs-on: nas
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -109,7 +110,7 @@ jobs:
|
|||||||
# 规则与豁免见 ci/scan-portable-sql.sh 头注 + docs/dev-conventions.html 支柱 3。
|
# 规则与豁免见 ci/scan-portable-sql.sh 头注 + docs/dev-conventions.html 支柱 3。
|
||||||
portable-sql-scan:
|
portable-sql-scan:
|
||||||
name: Portable SQL — 可移植性 (mysql/sqlite)
|
name: Portable SQL — 可移植性 (mysql/sqlite)
|
||||||
runs-on: nas
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -122,7 +123,7 @@ jobs:
|
|||||||
# (改了 design/colors_and_type.css 没重生成,或手改了生成物)。
|
# (改了 design/colors_and_type.css 没重生成,或手改了生成物)。
|
||||||
codegen-drift:
|
codegen-drift:
|
||||||
name: Codegen Drift — token 生成物未漂移
|
name: Codegen Drift — token 生成物未漂移
|
||||||
runs-on: nas
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -139,7 +140,7 @@ jobs:
|
|||||||
# -tags integration(需 docker 起 mysql/redis),见 go-integration job。
|
# -tags integration(需 docker 起 mysql/redis),见 go-integration job。
|
||||||
go-server:
|
go-server:
|
||||||
name: Go — build + test
|
name: Go — build + test
|
||||||
runs-on: nas
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -165,7 +166,7 @@ jobs:
|
|||||||
# 详见 scripts/e2e-smoke.sh + server/test/e2e/。
|
# 详见 scripts/e2e-smoke.sh + server/test/e2e/。
|
||||||
e2e-smoke:
|
e2e-smoke:
|
||||||
name: E2E Smoke — L4 进程级端到端
|
name: E2E Smoke — L4 进程级端到端
|
||||||
runs-on: nas
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -189,7 +190,7 @@ jobs:
|
|||||||
# · -p 1 串行:一次只起一个 mysql 容器,避免并发把 Docker Desktop 压垮/端口资源争用。
|
# · -p 1 串行:一次只起一个 mysql 容器,避免并发把 Docker Desktop 压垮/端口资源争用。
|
||||||
go-integration:
|
go-integration:
|
||||||
name: Go — integration (mysql/redis testcontainers)
|
name: Go — integration (mysql/redis testcontainers)
|
||||||
runs-on: nas
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -204,7 +205,7 @@ jobs:
|
|||||||
# tablet/desktop-stats golden 与 stats-overhaul 工作区耦合,待其合并后并入本 job。
|
# tablet/desktop-stats golden 与 stats-overhaul 工作区耦合,待其合并后并入本 job。
|
||||||
golden:
|
golden:
|
||||||
name: Golden — 视觉回归 (components + auth)
|
name: Golden — 视觉回归 (components + auth)
|
||||||
runs-on: nas
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy-server:
|
deploy-server:
|
||||||
runs-on: nas
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -12,18 +12,20 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy-site:
|
deploy-site:
|
||||||
runs-on: nas
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Compile (Astro 官网, node:20 容器内构建)
|
# runner 镜像 catthehacker/ubuntu:act-latest 自带 node/npx,直接跑;
|
||||||
run: |
|
# 不用嵌套 docker run(job 容器内的 $PWD 在宿主上不存在,DinD 挂载会失败)。
|
||||||
docker run --rm -v "$PWD:/w" -w /w \
|
- name: Compile (Astro 官网)
|
||||||
-e SITE_URL=https://pangolin.yanmeiai.com \
|
|
||||||
node:20 bash scripts/ci/compile-site.sh
|
|
||||||
|
|
||||||
- name: Deploy → pangolin1
|
|
||||||
env:
|
env:
|
||||||
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
SITE_URL: https://pangolin.yanmeiai.com
|
||||||
|
run: bash scripts/ci/compile-site.sh
|
||||||
|
|
||||||
|
- name: Deploy → Cloudflare Pages
|
||||||
|
env:
|
||||||
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
run: bash scripts/ci/deploy-site.sh
|
run: bash scripts/ci/deploy-site.sh
|
||||||
|
|||||||
@@ -111,10 +111,12 @@ pangolin.yanmeiai.com)与多端产物。现状:仅 <code>ci.yml</code> 校
|
|||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>4. 官网部署</h2>
|
<h2>4. 官网部署 —— Cloudflare Pages</h2>
|
||||||
|
<div class="card" style="border-left:3px solid var(--warn)">
|
||||||
|
<b>架构变更(2026-07-06 实施):</b>原计划 rsync 到 pangolin1 的 nginx。但节点 <code>:443</code> 被 sing-box(VPN 数据面)占用,而 CF 免费套餐 proxied 回源只能打 :80/:443、改回源端口需 Enterprise —— 同机同 IP 上官网 HTTPS 与 VPN 无法共存。<b>故官网改由 Cloudflare Pages 托管</b>:纯静态、全程 HTTPS、<code>_headers</code>/CSP 原生生效、不落 VPS,从根上无 :443 冲突,也不拖累 VPN 机器。<b>已上线</b> <code>https://pangolin.yanmeiai.com</code>。
|
||||||
|
</div>
|
||||||
<p>Astro <code>npm ci && npm run build</code>(<code>SITE_URL=https://pangolin.yanmeiai.com</code>)→ <code>dist/</code>
|
<p>Astro <code>npm ci && npm run build</code>(<code>SITE_URL=https://pangolin.yanmeiai.com</code>)→ <code>dist/</code>
|
||||||
rsync 到服务器 web 根 → nginx/Caddy 静态托管。<b>DNS</b>:CF 加记录
|
经 <code>npx wrangler pages deploy</code> 发布到 Pages 项目 <b>pangolin-site</b>(自定义域 <code>pangolin.yanmeiai.com</code>,CNAME → <code>pangolin-site.pages.dev</code>,proxied)。需 secret <code>CLOUDFLARE_API_TOKEN</code>(带 Account>Pages>Edit)+ <code>CLOUDFLARE_ACCOUNT_ID</code>;deploy 步骤在 <code>node:20</code> 容器内跑 wrangler。灾备:产物仍纯静态,可另 rsync 到镜像。</p>
|
||||||
<code>pangolin.yanmeiai.com</code> → 服务器(首次部署时做,记 baize)。</p>
|
|
||||||
|
|
||||||
<h2>5. 下载链接闭环(30A)</h2>
|
<h2>5. 下载链接闭环(30A)</h2>
|
||||||
<p><code>web/website/src/config/site.ts</code> 增 <code>downloads:{ android, macos, windows }</code>,值为 Gitea release
|
<p><code>web/website/src/config/site.ts</code> 增 <code>downloads:{ android, macos, windows }</code>,值为 Gitea release
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ pangolin 现有 CI 仅 `.gitea/workflows/ci.yml`(nas,只校验无部署)+ `web/w
|
|||||||
- `test.sh <server|client>` —— `go test` / `flutter test`
|
- `test.sh <server|client>` —— `go test` / `flutter test`
|
||||||
- `notify.sh` —— 成功/失败 Telegram 通知(可选,复用节点监控 bot)
|
- `notify.sh` —— 成功/失败 Telegram 通知(可选,复用节点监控 bot)
|
||||||
- `compile-site.sh` / `compile-backend.sh` / `compile-android.sh` / `compile-macos.sh` / `compile-windows.sh`
|
- `compile-site.sh` / `compile-backend.sh` / `compile-android.sh` / `compile-macos.sh` / `compile-windows.sh`
|
||||||
- `deploy-site.sh` / `deploy-server.sh`(ssh pangolin1)
|
- `deploy-site.sh`(wrangler → CF Pages)/ `deploy-server.sh`(ssh pangolin1,复用 lib-ssh)
|
||||||
- `release-<x>.sh` —— 建 release + 挂产物
|
- `release-<x>.sh` —— 建 release + 挂产物
|
||||||
|
|
||||||
> 每个 `compile-*` 内部封装该端已验证的构建命令(如 Android 走
|
> 每个 `compile-*` 内部封装该端已验证的构建命令(如 Android 走
|
||||||
@@ -74,11 +74,18 @@ pangolin 现有 CI 仅 `.gitea/workflows/ci.yml`(nas,只校验无部署)+ `web/w
|
|||||||
5. `install` 新二进制到 `/usr/local/bin`(旧的备份为 `.bak-<tag>`)
|
5. `install` 新二进制到 `/usr/local/bin`(旧的备份为 `.bak-<tag>`)
|
||||||
6. `systemctl start pangolin-server` + `/healthz` 健康检查;agent 随连接自恢复
|
6. `systemctl start pangolin-server` + `/healthz` 健康检查;agent 随连接自恢复
|
||||||
|
|
||||||
### 4.4 官网部署(deploy-site.sh)
|
### 4.4 官网部署(deploy-site.sh)—— Cloudflare Pages
|
||||||
|
|
||||||
Astro `npm ci && npm run build`(`SITE_URL=https://pangolin.yanmeiai.com`)→ `dist/` rsync 到
|
> **架构变更(2026-07-06 实施):** 原计划 rsync 到 pangolin1 的 nginx。但节点 :443 被 sing-box
|
||||||
服务器 web 根 → 由 nginx/Caddy 静态托管 `pangolin.yanmeiai.com`。**DNS**:CF 加 A/CNAME
|
> (VPN 数据面)占用,而 CF 免费套餐 proxied 回源只能打 :80/:443、改回源端口需 Enterprise ——
|
||||||
`pangolin.yanmeiai.com` → 服务器(首次部署时做,记 baize)。
|
> 无法在同机同 IP 上让官网 HTTPS 与 VPN 共存。**故官网改由 Cloudflare Pages 托管**:纯静态、
|
||||||
|
> 全程 HTTPS、`_headers`/CSP 原生生效、不落 VPS,从根上无 :443 冲突,也不拖累 VPN 机器。
|
||||||
|
|
||||||
|
Astro `npm ci && npm run build`(`SITE_URL=https://pangolin.yanmeiai.com`)→ `dist/` 经
|
||||||
|
`npx wrangler pages deploy` 发布到 CF Pages 项目 **`pangolin-site`**(自定义域
|
||||||
|
`pangolin.yanmeiai.com`,CNAME → `pangolin-site.pages.dev`,proxied)。
|
||||||
|
需 secret:`CLOUDFLARE_API_TOKEN`(带 Account>Pages>Edit)+ `CLOUDFLARE_ACCOUNT_ID`(账户级)。
|
||||||
|
deploy 步骤在 `node:20` 容器内跑 wrangler。**灾备**:构建产物仍是纯静态,可另 rsync 到任意镜像。
|
||||||
|
|
||||||
## 5. 下载链接闭环(30A)
|
## 5. 下载链接闭环(30A)
|
||||||
|
|
||||||
|
|||||||
+17
-20
@@ -1,32 +1,29 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# deploy-site.sh — rsync the built Astro 官网 (web/website/dist/) to the
|
# deploy-site.sh — 部署构建好的 Astro 官网 (web/website/dist/) 到 Cloudflare Pages。
|
||||||
# pangolin1 VPS web root (/var/www/pangolin-site). Deploy user is root — the
|
#
|
||||||
# CI deploy key (secrets.DEPLOY_SSH_KEY) is authorized for root on the host.
|
# 官网托管在 CF Pages(项目 pangolin-site,自定义域 pangolin.yanmeiai.com),纯静态、
|
||||||
# Static files only; nginx/caddy serves them directly, no service restart
|
# 全程 HTTPS、CSP(_headers)自动生效,不落在 VPS 上 —— 故与节点 :443(sing-box)无冲突。
|
||||||
# needed. Assumes it is invoked from the repo root, after compile-site.sh has
|
#
|
||||||
# produced web/website/dist/.
|
# 需环境变量:
|
||||||
|
# CLOUDFLARE_API_TOKEN 带 Account > Cloudflare Pages > Edit 权限的 CF token
|
||||||
|
# CLOUDFLARE_ACCOUNT_ID CF 账户 ID
|
||||||
|
# 由 compile-site.sh 先产出 web/website/dist/;从 repo 根调用。
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# shellcheck source=scripts/ci/lib-ssh.sh
|
|
||||||
. scripts/ci/lib-ssh.sh
|
|
||||||
|
|
||||||
# setup_ssh registers the EXIT cleanup trap itself (before writing the key),
|
|
||||||
# so a mid-setup failure still cleans up — see lib-ssh.sh.
|
|
||||||
setup_ssh
|
|
||||||
|
|
||||||
# Guard against deploying a missing/empty build: `rsync --delete` against an
|
|
||||||
# empty source would wipe the live site.
|
|
||||||
if [ ! -d web/website/dist ]; then
|
if [ ! -d web/website/dist ]; then
|
||||||
echo "==> deploy-site: web/website/dist/ does not exist — refusing to deploy" >&2
|
echo "==> deploy-site: web/website/dist/ 不存在 — 拒绝部署" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! find web/website/dist -mindepth 1 -print -quit | grep -q .; then
|
if ! find web/website/dist -mindepth 1 -print -quit | grep -q .; then
|
||||||
echo "==> deploy-site: web/website/dist/ is empty — refusing to deploy" >&2
|
echo "==> deploy-site: web/website/dist/ 为空 — 拒绝部署" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DEPLOY_TARGET="root@${DEPLOY_HOST}:/var/www/pangolin-site/"
|
: "${CLOUDFLARE_API_TOKEN:?deploy-site: CLOUDFLARE_API_TOKEN 未设(需带 Pages:Edit)}"
|
||||||
echo "==> deploy-site: rsync web/website/dist/ -> ${DEPLOY_TARGET}"
|
: "${CLOUDFLARE_ACCOUNT_ID:?deploy-site: CLOUDFLARE_ACCOUNT_ID 未设}"
|
||||||
rsync -az --delete -e "${RSYNC_SSH}" web/website/dist/ "${DEPLOY_TARGET}"
|
|
||||||
|
echo "==> deploy-site: wrangler pages deploy → project pangolin-site (branch main)"
|
||||||
|
npx --yes wrangler@4 pages deploy web/website/dist \
|
||||||
|
--project-name=pangolin-site --branch=main --commit-dirty=true
|
||||||
|
|
||||||
echo "==> deploy-site: done"
|
echo "==> deploy-site: done"
|
||||||
|
|||||||
Executable
+49
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# test.sh <server|client> — run the test suite for one side of the repo.
|
||||||
|
# Invoked directly on the nas runner (see deploy-server.yml's "Test" step),
|
||||||
|
# so this script itself owns the docker invocation — Go/Flutter are not on
|
||||||
|
# the host.
|
||||||
|
#
|
||||||
|
# server: `go test ./...` inside golang:1.25, mirroring the go-server job's
|
||||||
|
# docker invocation in .gitea/workflows/ci.yml (mounts + gomod/gobuild
|
||||||
|
# caches), plus GOPROXY from _env.sh so it doesn't hit proxy.golang.org.
|
||||||
|
# client: `flutter test` inside ghcr.io/cirruslabs/flutter:stable, mirroring
|
||||||
|
# the flutter-client job's docker invocation in .gitea/workflows/ci.yml
|
||||||
|
# (pub-cache volume, same test dirs); analyze/coverage stay in that job.
|
||||||
|
#
|
||||||
|
# Run from the repo root (relative paths below assume this).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# shellcheck source=scripts/ci/_env.sh
|
||||||
|
. scripts/ci/_env.sh
|
||||||
|
|
||||||
|
TARGET="${1:-}"
|
||||||
|
|
||||||
|
case "$TARGET" in
|
||||||
|
server)
|
||||||
|
mkdir -p "$HOME/.cache/pangolin-ci/gomod" "$HOME/.cache/pangolin-ci/gobuild"
|
||||||
|
echo "==> test: go test ./... (golang:1.25 容器)"
|
||||||
|
docker run --rm \
|
||||||
|
-v "$PWD/server:/app" -w /app \
|
||||||
|
-v "$HOME/.cache/pangolin-ci/gomod:/go/pkg/mod" \
|
||||||
|
-v "$HOME/.cache/pangolin-ci/gobuild:/root/.cache/go-build" \
|
||||||
|
-e GOPROXY="$GOPROXY" \
|
||||||
|
golang:1.25 \
|
||||||
|
go test ./...
|
||||||
|
;;
|
||||||
|
client)
|
||||||
|
mkdir -p "$HOME/.cache/pangolin-ci/pubcache"
|
||||||
|
echo "==> test: flutter test (ghcr.io/cirruslabs/flutter:stable 容器)"
|
||||||
|
docker run --rm \
|
||||||
|
-v "$PWD/client:/app" -w /app \
|
||||||
|
-v "$HOME/.cache/pangolin-ci/pubcache:/root/.pub-cache" \
|
||||||
|
ghcr.io/cirruslabs/flutter:stable \
|
||||||
|
bash -c "flutter pub get && flutter test test/unit test/widget test/contract"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "usage: scripts/ci/test.sh <server|client>" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "==> test: ${TARGET} 通过"
|
||||||
Reference in New Issue
Block a user