refactor(server): 拆分 cmd/mini 与 cmd/web 两个部署入口
小程序接口(lunar.neatcn.com)与商业版 Web(kepo.net)此前共用一个 二进制,导致小程序域名暴露商业后台与 Stripe Webhook,商业站点也 暴露了许愿树等小程序接口。现拆为两个入口: - cmd/mini:微信登录、生辰档案、许愿树、百科、版本号 - cmd/web:邮箱登录、commerce、报告/订单、/admin 后台、商业前端 静态托管与 SPA 回退 - internal/app 承载共用启动与路由注册;图片资源移至 internal/assets CI:server-deploy.yml 改构建 cmd/mini 并随包移除后台静态资源; 新增 web-deploy.yml 构建 cmd/web + web-commercial 前端,发布 web-v* Release;deploy-watch.sh 支持 TAG_PREFIX/ASSET_NAME 变量; 新增 deploy-web.sh(systemd 版)与 kepo 一次性配置文档 .gitea/docs/PULL_DEPLOY_WEB.md。 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent
28309e0257
commit
12fa2b0d19
@@ -0,0 +1,270 @@
|
||||
name: Build and Publish Commercial Web
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "server/**"
|
||||
- "web/**"
|
||||
- "web-commercial/**"
|
||||
- ".gitea/workflows/web-deploy.yml"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# 标准标签:hk / bt-runner 均带 ubuntu-latest,任一空闲 runner 接单
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# 精简 runner(Alpine)无 node,无法跑 action-checkout;直接用 git 浅克隆
|
||||
- name: Checkout
|
||||
env:
|
||||
REPO_URL: ${{ gitea.server_url }}/${{ gitea.repository }}
|
||||
REF_NAME: ${{ gitea.ref_name }}
|
||||
run: |
|
||||
git init -q .
|
||||
git remote add origin "$REPO_URL"
|
||||
git fetch -q --depth 20 origin "$REF_NAME"
|
||||
git checkout -q FETCH_HEAD
|
||||
git log --oneline -3
|
||||
|
||||
- name: Ensure curl available
|
||||
run: |
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
if command -v apk >/dev/null 2>&1; then apk add --no-cache curl
|
||||
elif command -v apt-get >/dev/null 2>&1; then apt-get update -qq && apt-get install -y -qq curl
|
||||
elif command -v dnf >/dev/null 2>&1; then dnf install -y -q curl
|
||||
else echo "无法安装 curl"; exit 1; fi
|
||||
fi
|
||||
curl --version | head -1
|
||||
|
||||
- name: Prepare Go toolchain
|
||||
run: |
|
||||
TOOLDIR="$RUNNER_WORKSPACE/.lunar-toolchain"
|
||||
mkdir -p "$TOOLDIR"
|
||||
if [ -x "$TOOLDIR/go/bin/go" ]; then
|
||||
echo "复用缓存 Go 工具链"
|
||||
else
|
||||
wget -q --tries=2 --timeout=60 -O "$TOOLDIR/go.tgz" \
|
||||
"https://go.dev/dl/go1.22.10.linux-amd64.tar.gz" \
|
||||
|| wget -q --tries=2 --timeout=60 -O "$TOOLDIR/go.tgz" \
|
||||
"https://mirrors.aliyun.com/golang/go1.22.10.linux-amd64.tar.gz"
|
||||
rm -rf "$TOOLDIR/go"
|
||||
tar -C "$TOOLDIR" -xzf "$TOOLDIR/go.tgz"
|
||||
rm -f "$TOOLDIR/go.tgz"
|
||||
fi
|
||||
"$TOOLDIR/go/bin/go" version
|
||||
echo "LUNAR_GO=$TOOLDIR/go/bin/go" >> "$GITEA_ENV"
|
||||
|
||||
- name: Prepare Node toolchain
|
||||
run: |
|
||||
TOOLDIR="$RUNNER_WORKSPACE/.lunar-toolchain"
|
||||
mkdir -p "$TOOLDIR"
|
||||
if [ -x "$TOOLDIR/node/bin/node" ] && "$TOOLDIR/node/bin/node" --version >/dev/null 2>&1; then
|
||||
echo "复用缓存 Node 工具链"
|
||||
else
|
||||
rm -rf "$TOOLDIR/node" "$TOOLDIR/node.tgz"
|
||||
mkdir -p "$TOOLDIR"
|
||||
if ldd --version 2>&1 | grep -qi musl || [ ! -e /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 ]; then
|
||||
NODE_DIST="node-v22.12.0-linux-x64-musl"
|
||||
NODE_SHA="8d96de0293fc3978d09e2a3fee68c66bca4efcca90f52105e6f7463636e97308"
|
||||
URLS="https://registry.npmmirror.com/-/binary/node-unofficial-builds/v22.12.0/$NODE_DIST.tar.gz
|
||||
https://unofficial-builds.nodejs.org/download/release/v22.12.0/$NODE_DIST.tar.gz"
|
||||
else
|
||||
NODE_DIST="node-v22.12.0-linux-x64"
|
||||
NODE_SHA="e05a4d65232ae2b27b3d77da2e368522fb46b923335b8e0d5f77624c32484044"
|
||||
URLS="https://nodejs.org/dist/v22.12.0/$NODE_DIST.tar.gz
|
||||
https://mirrors.aliyun.com/nodejs-release/v22.12.0/$NODE_DIST.tar.gz"
|
||||
fi
|
||||
DL_OK=""
|
||||
for url in $URLS; do
|
||||
echo "尝试下载: $url"
|
||||
if wget -t 2 -T 120 -q -O "$TOOLDIR/node.tgz" "$url"; then
|
||||
if [ "$(sha256sum "$TOOLDIR/node.tgz" | cut -d' ' -f1)" = "$NODE_SHA" ]; then
|
||||
echo "下载成功且校验通过"
|
||||
DL_OK=1
|
||||
break
|
||||
fi
|
||||
echo "sha256 校验失败,尝试下一源"
|
||||
else
|
||||
echo "wget 下载失败,尝试下一源"
|
||||
fi
|
||||
rm -f "$TOOLDIR/node.tgz"
|
||||
done
|
||||
test -n "$DL_OK" || { echo "::error::所有下载源均失败或校验不通过"; exit 1; }
|
||||
tar -C "$TOOLDIR" -xzf "$TOOLDIR/node.tgz"
|
||||
mv "$TOOLDIR/$NODE_DIST" "$TOOLDIR/node"
|
||||
rm -f "$TOOLDIR/node.tgz"
|
||||
if [ "$NODE_DIST" = "node-v22.12.0-linux-x64-musl" ] && [ ! -e /usr/lib/libstdc++.so.6 ] && [ ! -e /lib/libstdc++.so.6 ]; then
|
||||
if command -v apk >/dev/null 2>&1; then apk add --no-cache libstdc++ libgcc || echo "::warning::libstdc++/libgcc 安装失败"; fi
|
||||
fi
|
||||
fi
|
||||
if ! "$TOOLDIR/node/bin/node" --version; then
|
||||
echo "::error::Node 二进制无法执行"
|
||||
rm -rf "$TOOLDIR/node"
|
||||
exit 1
|
||||
fi
|
||||
echo "PATH=$TOOLDIR/node/bin:$PATH" >> "$GITEA_ENV"
|
||||
|
||||
- name: Install pnpm
|
||||
run: |
|
||||
# pnpm 随工具链缓存,避免每次重复下载
|
||||
TOOLDIR="$RUNNER_WORKSPACE/.lunar-toolchain"
|
||||
if [ ! -x "$TOOLDIR/pnpm/bin/pnpm" ]; then
|
||||
npm install -g --prefix "$TOOLDIR/pnpm" pnpm@9
|
||||
fi
|
||||
echo "PATH=$TOOLDIR/pnpm/bin:$PATH" >> "$GITEA_ENV"
|
||||
pnpm --version
|
||||
|
||||
- name: Run Go tests
|
||||
working-directory: server
|
||||
run: |
|
||||
export HOME="$RUNNER_WORKSPACE"
|
||||
"$LUNAR_GO" mod download
|
||||
"$LUNAR_GO" test ./...
|
||||
|
||||
- name: Build commercial frontend
|
||||
env:
|
||||
CI: "true"
|
||||
run: |
|
||||
export HOME="$RUNNER_WORKSPACE"
|
||||
# web/ 工作区:装依赖并构建 @lunar/core(web-commercial 的 file: 依赖)
|
||||
cd web
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm --filter @lunar/core build
|
||||
cd ../web-commercial
|
||||
pnpm install --frozen-lockfile
|
||||
npx tsc -b
|
||||
npx vite build
|
||||
test -f dist/index.html
|
||||
|
||||
- name: Build web server binary
|
||||
working-directory: server
|
||||
env:
|
||||
GITEA_SHA: ${{ gitea.sha }}
|
||||
GITEA_RUN_NUMBER: ${{ gitea.run_number }}
|
||||
run: |
|
||||
export HOME="$RUNNER_WORKSPACE"
|
||||
mkdir -p bin
|
||||
|
||||
VERSION="1.1.${GITEA_RUN_NUMBER}"
|
||||
BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
COMMIT_SHORT="${GITEA_SHA:0:7}"
|
||||
|
||||
echo "Building web server version: $VERSION"
|
||||
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 "$LUNAR_GO" build -ldflags "\
|
||||
-X main.Version=$VERSION \
|
||||
-X main.BuildTime=$BUILD_TIME \
|
||||
-X main.CommitSha=$COMMIT_SHORT" \
|
||||
-o bin/server ./cmd/web
|
||||
|
||||
cat > bin/version.env <<EOF
|
||||
APP_VERSION=$VERSION
|
||||
APP_BUILD_TIME=$BUILD_TIME
|
||||
APP_COMMIT_SHA=$COMMIT_SHORT
|
||||
EOF
|
||||
cat bin/version.env
|
||||
|
||||
- name: Create deployment package
|
||||
working-directory: server
|
||||
run: |
|
||||
mkdir -p deploy
|
||||
cp -r bin/ deploy/
|
||||
cp -r migrations/ deploy/
|
||||
cp -r scripts/ deploy/
|
||||
# 商业前端产物:web 二进制以相对路径 ./frontend 提供
|
||||
cp -r ../web-commercial/dist deploy/frontend/
|
||||
# 管理后台静态资源与模板(服务以相对路径 ./web 提供)
|
||||
cp -r web/ deploy/web/
|
||||
tar -czf web-deploy.tar.gz deploy/
|
||||
sha256sum web-deploy.tar.gz
|
||||
|
||||
- name: Publish release with deployment package
|
||||
env:
|
||||
GITEA_SHA: ${{ gitea.sha }}
|
||||
GITEA_RUN_NUMBER: ${{ gitea.run_number }}
|
||||
LUNAR_PUBLISH_TOKEN: ${{ secrets.LUNAR_PUBLISH_TOKEN }}
|
||||
run: |
|
||||
set -e
|
||||
VERSION="1.1.${GITEA_RUN_NUMBER}"
|
||||
TAG="web-v${VERSION}"
|
||||
API="https://gitea.neatcn.com/api/v1/repos/gouki/lunar-mini"
|
||||
TOKEN="${LUNAR_PUBLISH_TOKEN:-$GITEA_TOKEN}"
|
||||
test -n "$TOKEN"
|
||||
AUTH="Authorization: token $TOKEN"
|
||||
|
||||
EXISTING=$(curl -sS -H "$AUTH" "$API/releases/tags/$TAG" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2 || true)
|
||||
if [ -n "$EXISTING" ]; then
|
||||
curl -sS -X DELETE -H "$AUTH" "$API/releases/$EXISTING" -o /dev/null -w "删除旧 release: HTTP %{http_code}\n"
|
||||
fi
|
||||
|
||||
CREATE_RESP=$(curl -sS -X POST -H "$AUTH" -H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\":\"$TAG\",\"name\":\"web $VERSION\",\"target_commitish\":\"main\",\"body\":\"CI 自动构建,commit ${GITEA_SHA:0:7}。kepo 服务器由 deploy-watch.sh(TAG_PREFIX=web-v)拉取本附件完成部署。\"}" \
|
||||
"$API/releases")
|
||||
RELEASE_ID=$(printf '%s' "$CREATE_RESP" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2 || true)
|
||||
if [ -z "$RELEASE_ID" ]; then
|
||||
echo "创建 release 失败,API 响应: $CREATE_RESP"
|
||||
exit 1
|
||||
fi
|
||||
echo "Release created: id=$RELEASE_ID tag=$TAG"
|
||||
|
||||
curl -sS -X POST -H "$AUTH" \
|
||||
-F "attachment=@server/web-deploy.tar.gz" \
|
||||
"$API/releases/$RELEASE_ID/assets" \
|
||||
-o /dev/null -w "附件上传: HTTP %{http_code}\n"
|
||||
echo "发布地址: https://gitea.neatcn.com/gouki/lunar-mini/releases/tag/$TAG"
|
||||
|
||||
- name: Cleanup old web releases (keep latest 10)
|
||||
env:
|
||||
LUNAR_PUBLISH_TOKEN: ${{ secrets.LUNAR_PUBLISH_TOKEN }}
|
||||
run: |
|
||||
API="https://gitea.neatcn.com/api/v1/repos/gouki/lunar-mini"
|
||||
TOKEN="${LUNAR_PUBLISH_TOKEN:-$GITEA_TOKEN}"
|
||||
AUTH="Authorization: token $TOKEN"
|
||||
KEEP=10
|
||||
|
||||
PAIRS=$(curl -sS -H "$AUTH" "$API/releases?limit=50" \
|
||||
| grep -oE '"id":[0-9]+,"tag_name":"web-v[^"]*"' || true)
|
||||
TOTAL=$(printf '%s\n' "$PAIRS" | grep -c '"id"' || true)
|
||||
if [ "$TOTAL" -le "$KEEP" ]; then
|
||||
echo "当前 $TOTAL 个发布版本,未超过 $KEEP,无需清理"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
printf '%s\n' "$PAIRS" | tail -n +$((KEEP + 1)) | while IFS= read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
RID=$(printf '%s' "$line" | grep -oE '"id":[0-9]+' | cut -d: -f2)
|
||||
RTAG=$(printf '%s' "$line" | sed 's/.*"tag_name":"\([^"]*\)".*/\1/')
|
||||
curl -sS -X DELETE -H "$AUTH" "$API/releases/$RID" -o /dev/null -w "删除旧发布 $RTAG: HTTP %{http_code}\n"
|
||||
curl -sS -X DELETE -H "$AUTH" "$API/tags/$RTAG" -o /dev/null -w "删除 tag $RTAG: HTTP %{http_code}\n"
|
||||
done
|
||||
|
||||
- name: Send success notification
|
||||
if: success()
|
||||
env:
|
||||
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
GITEA_RUN_NUMBER: ${{ gitea.run_number }}
|
||||
run: |
|
||||
VERSION="1.1.${GITEA_RUN_NUMBER}"
|
||||
bash .gitea/scripts/notify.sh success \
|
||||
"商业版 Web 构建发布成功" \
|
||||
"版本: v${VERSION}\n产物: releases/tag/web-v${VERSION}\nkepo 服务器将自动拉取部署" || echo "通知发送失败,不影响主流程"
|
||||
|
||||
- name: Send failure notification
|
||||
if: failure()
|
||||
env:
|
||||
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
run: |
|
||||
bash .gitea/scripts/notify.sh failure \
|
||||
"商业版 Web 构建失败" \
|
||||
"请检查 Actions 日志了解失败原因" || echo "通知发送失败"
|
||||
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
run: |
|
||||
rm -rf server/deploy/
|
||||
rm -f server/web-deploy.tar.gz
|
||||
Reference in New Issue
Block a user