Files
lunar-mini/.gitea/workflows/web-deploy.yml
T
goukiandDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> 12fa2b0d19
Build and Publish Commercial Web / build (push) Failing after 1m25s
Build and Publish Server / build (push) Successful in 3m25s
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>
2026-10-18 11:31:51 +00:00

271 lines
11 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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