部署:push origin 就由开发机编好、测过再传到服务器,服务器不再自己编,push 后一两分钟上线。照 OJ2 的做法,开发机上的 gitea-runner(和 OJ2 共用,标签 iot-deploy,一次只跑一个任务)跑 .gitea/workflows/deploy.yml:编 C# 运行时(Release)、网页和 server.js,跑服务端测试(用要上线的 Release 版运行时)、网页端测试和类型检查,产物 rsync 到服务器的 prebuilt/(按内容比、传完才换上、断了重试),服务器上 deploy/update.sh --prebuilt 用现成产物构建镜像,只剩几条 COPY。以前 GitHub 上测试约 5 分钟、服务器 2 核再编约一分钟(编的时候学生程序会卡),push 后 6 分钟多才上线。host 模式用开发机自己的 ssh 和 git 访问,不需要 secret。Dockerfile 加构建参数 ARTIFACTS(build 默认在镜像里编 / prebuilt 用现成产物),服务器自己编的能力保留;prebuilt/COMMIT 和要部署的提交对不上时 update.sh 直接失败。服务端测试同时跑:文件之间 --parallel,评分测试标 describe.concurrent(--max-concurrency=8),本机 4 分钟降到半分钟,连跑三遍都通过。GitHub 的 ci.yml 改成 PR 跑测试、手动触发才部署(服务器自己编),开发机不在时用,push 不再两边各部署一遍。顺手:update.sh 每次部署拉一下 caddy:2-alpine(up 不会自己拉,一直是第一次部署时那份);app 和 caddy 的容器日志限 10MB × 3。部署说明、README、设计方案同步
Deploy / deploy (push) Successful in 1m38s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-11-03 12:08:14 +08:00
1 parent 77bfaba7ff
commit 4c3ce31fe1
10 files changed
+274 -69

No files matched your search

+37 -5
View File
@@ -1,14 +1,27 @@
#!/usr/bin/env bash
# 在服务器上执行:拉取代码、构建镜像、重启,等到编译服务就绪才算成功。
# GitHub Actions 测试通过后通过 SSH 调用它,也可以登录服务器手动执行。
# 用法:deploy/update.sh [提交号,默认 origin/main]
# 用法:deploy/update.sh [提交号,默认 origin/main] [--prebuilt]
#
# 不带 --prebuilt:服务器自己编(只要有 docker 就能跑,不依赖开发机;2 核编一次约一分钟,期间学生程序会卡)。
# GitHub 上手动触发的备用部署、登录服务器手动部署都走这条。
# --prebuilt: 用 prebuilt/ 里现成的产物,镜像构建只剩几条 COPY。push origin 后开发机上的 gitea-runner
# 编好、测过、rsync 过来再调用(.gitea/workflows/deploy.yml)。prebuilt/COMMIT 必须就是要部署的提交,
# 对不上说明产物不是这次编的,直接失败,不会把旧产物配新代码。
#
# 第一次部署前,按 deploy/README.md 在服务器上克隆仓库、准备好 .env
set -euo pipefail
# 整个脚本包在函数里:git reset 会在运行中替换本文件,bash 先读完函数再执行,不受影响
main() {
cd "$(dirname "$0")/.."
local ref=${1:-origin/main}
local ref=origin/main prebuilt=0 arg
for arg in "$@"; do
case "$arg" in
--prebuilt) prebuilt=1 ;;
-*) echo "未知参数:$arg(可用:--prebuilt)" >&2; exit 2 ;;
*) ref=$arg ;;
esac
done
if [[ ! -f .env ]]; then
echo '还没有 .env:cp .env.example .env 后设置 TEACHER_PASSWORD'
@@ -16,8 +29,7 @@ main() {
fi
echo "== 拉取代码"
# 代码从 origin(git.xuyue.cc)拉取。Actions 由推送到 GitHub 触发,
# 两个仓库推送有先后,这个提交可能还没到 origin,多等一会儿
# 代码从 origin(git.xuyue.cc)拉取。GitHub 上手动触发时,这个提交可能还没推到 origin,多等一会儿
for attempt in $(seq 10); do
git fetch --quiet origin
if git rev-parse --quiet --verify "$ref^{commit}" >/dev/null; then break; fi
@@ -33,7 +45,27 @@ main() {
git reset --quiet --hard "$ref"
git log --oneline -1
if [[ $prebuilt == 1 ]]; then
local head built
head=$(git rev-parse HEAD)
built=$(cat prebuilt/COMMIT 2>/dev/null || true)
if [[ $built != "$head" ]]; then
echo "prebuilt/ 里的产物是 ${built:-(没有)} 编的,要部署的是 $head。"
echo '重新跑一次开发机上的部署,或者去掉 --prebuilt 让服务器自己编'
exit 1
fi
for f in prebuilt/runtime/Sim.Runner.dll prebuilt/web/index.html prebuilt/server.js; do
[[ -e $f ]] || { echo "prebuilt/ 不完整,缺 $f"; exit 1; }
done
export ARTIFACTS=prebuilt
echo "== 用开发机编好的产物($(du -sh prebuilt | cut -f1))"
else
export ARTIFACTS=build
fi
echo "== 构建镜像并重启"
# caddy 用的是现成镜像,up 不会去拉新版,不拉的话永远是第一次部署时那份
docker compose pull --quiet caddy
docker compose up -d --build --remove-orphans
docker image prune -f >/dev/null