部署:update.sh 改由 deploy/update-bg.sh 在服务器上后台跑(setsid),SSH 断了它照样跑完。2026-11-03 开发机上 FlClash 切换配置重建了 TUN 网卡,SSH 正好断在 docker compose up 中间:旧容器删了、新容器只创建没启动,网站停了 40 分钟。输出写到 /var/tmp/iot_sim-update.log,--follow <提交号> 接着看输出、退出码和 update.sh 一样,这个提交的部署还没开始时返回 75;上一次还在跑就等它结束。Gitea 的"起服务"ssh 返回 255 就 10 秒后重连 --follow,75 就重新开始,最多 6 次。GitHub 备用部署也改用 update-bg.sh。部署说明同步
Deploy / deploy (push) Successful in 1m28s
Deploy / deploy (push) Successful in 1m28s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2aa267839f
commit
f242308a3b
4 files changed
+85
-4
No files matched your search
@@ -110,9 +110,26 @@ jobs:
|
|||||||
|
|
||||||
# 先把服务器上的代码切到这个提交再跑 update.sh:不然跑的是上一版的 update.sh,改了它要晚一次部署才生效。
|
# 先把服务器上的代码切到这个提交再跑 update.sh:不然跑的是上一版的 update.sh,改了它要晚一次部署才生效。
|
||||||
# 重启会结束正在运行的学生程序,上课时间尽量不要推送 main。
|
# 重启会结束正在运行的学生程序,上课时间尽量不要推送 main。
|
||||||
|
#
|
||||||
|
# update.sh 由 update-bg.sh 在服务器上后台跑,SSH 断了(开发机切代理、换网络)它照样跑完;
|
||||||
|
# 这边 ssh 返回 255(连接出错)就重连,用 --follow 接着看、拿退出码。
|
||||||
|
# 75 = 服务器上这个提交的部署还没开始(断在 git fetch 那里),重新开始。
|
||||||
- name: 起服务
|
- name: 起服务
|
||||||
|
timeout-minutes: 10
|
||||||
env:
|
env:
|
||||||
SHA: ${{ gitea.sha }}
|
SHA: ${{ gitea.sha }}
|
||||||
run: |
|
run: |
|
||||||
ssh -o BatchMode=yes -o ServerAliveInterval=15 -o ServerAliveCountMax=8 "$REMOTE" \
|
remote() { ssh -o BatchMode=yes -o ConnectTimeout=20 -o ServerAliveInterval=15 -o ServerAliveCountMax=8 "$REMOTE" "$1"; }
|
||||||
"cd $REMOTE_DIR && git fetch --quiet origin && git reset --quiet --hard $SHA && deploy/update.sh $SHA --prebuilt"
|
start="cd $REMOTE_DIR && git fetch --quiet origin && git reset --quiet --hard $SHA && deploy/update-bg.sh $SHA --prebuilt"
|
||||||
|
follow="cd $REMOTE_DIR && deploy/update-bg.sh --follow $SHA"
|
||||||
|
code=0; remote "$start" || code=$?
|
||||||
|
for attempt in 1 2 3 4 5 6; do
|
||||||
|
case $code in
|
||||||
|
255) echo "::warning::和服务器的 SSH 断了,部署在服务器上接着跑,10 秒后重连接着看"
|
||||||
|
sleep 10; code=0; remote "$follow" || code=$? ;;
|
||||||
|
75) echo "::warning::服务器上的部署还没开始,重新开始"
|
||||||
|
code=0; remote "$start" || code=$? ;;
|
||||||
|
*) exit $code ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
exit $code
|
||||||
@@ -100,4 +100,4 @@ jobs:
|
|||||||
|
|
||||||
- name: 部署(在服务器上拉代码、构建、重启)
|
- name: 部署(在服务器上拉代码、构建、重启)
|
||||||
if: steps.check.outputs.configured == 'true'
|
if: steps.check.outputs.configured == 'true'
|
||||||
run: ssh deploy-target "cd ${{ vars.DEPLOY_DIR || 'IOT_SIM' }} && deploy/update.sh ${{ github.sha }}"
|
run: ssh deploy-target "cd ${{ vars.DEPLOY_DIR || 'IOT_SIM' }} && deploy/update-bg.sh ${{ github.sha }}"
|
||||||
+2
-1
@@ -70,6 +70,7 @@
|
|||||||
|
|
||||||
`.gitea/workflows/deploy.yml`:push 到 origin(git.xuyue.cc)的 main 时,**开发机上的 gitea-runner** 编 C# 运行时、网页和 `server.js`,跑服务端测试(含沙箱)、网页端测试和类型检查(有一项不过就不部署),把产物 rsync 到服务器的 `~/IOT_SIM/prebuilt/`,再在服务器上执行 `deploy/update.sh <提交号> --prebuilt`:拉代码、用现成产物构建镜像(只剩几条 COPY)、重启,等到"编译服务已就绪"才算成功。push 后一两分钟上线,进度在 git.xuyue.cc/xuyue/IOT_SIM/actions。
|
`.gitea/workflows/deploy.yml`:push 到 origin(git.xuyue.cc)的 main 时,**开发机上的 gitea-runner** 编 C# 运行时、网页和 `server.js`,跑服务端测试(含沙箱)、网页端测试和类型检查(有一项不过就不部署),把产物 rsync 到服务器的 `~/IOT_SIM/prebuilt/`,再在服务器上执行 `deploy/update.sh <提交号> --prebuilt`:拉代码、用现成产物构建镜像(只剩几条 COPY)、重启,等到"编译服务已就绪"才算成功。push 后一两分钟上线,进度在 git.xuyue.cc/xuyue/IOT_SIM/actions。
|
||||||
|
|
||||||
|
- `update.sh` 由 `deploy/update-bg.sh` 在服务器上**后台**跑,SSH 断了(开发机切代理、换网络)它照样跑完,runner 重连后接着看输出、拿结果。以前它跑在 SSH 会话里,断线时正好在 `docker compose up` 中间,旧容器删了、新容器没启动,网站就停了。服务器上的部署输出在 `/var/tmp/iot_sim-update.log`。
|
||||||
- **重启会结束正在运行的学生程序**,上课时间尽量不要推送 main。
|
- **重启会结束正在运行的学生程序**,上课时间尽量不要推送 main。
|
||||||
- **push 时开发机得开着。** 没开的话任务在 Gitea 里排队,开机后 runner 连上就接着跑;排队超过 24 小时会被作废,要在 Actions 页面重跑。
|
- **push 时开发机得开着。** 没开的话任务在 Gitea 里排队,开机后 runner 连上就接着跑;排队超过 24 小时会被作废,要在 Actions 页面重跑。
|
||||||
- 在 Gitea 的 Actions 页面点 Run workflow 可以手动重新部署(比如改了服务器上的 `.env` 之后)。
|
- 在 Gitea 的 Actions 页面点 Run workflow 可以手动重新部署(比如改了服务器上的 `.env` 之后)。
|
||||||
@@ -93,7 +94,7 @@ host 模式直接用开发机自己的东西,所以**不需要配任何 secret
|
|||||||
|
|
||||||
### 备用:GitHub 手动触发
|
### 备用:GitHub 手动触发
|
||||||
|
|
||||||
`.github/workflows/ci.yml` 只在 PR 时跑测试,push 不触发(不然每次推送两边各部署一遍)。开发机不在、又急着上线时,在 github.com 的 Actions → CI/CD → Run workflow:测试通过后 SSH 到服务器执行 `deploy/update.sh <提交号>`,**服务器自己编**。这个提交要已经推到 origin(服务器从 origin 拉代码,没到时脚本会等最多约 100 秒)。
|
`.github/workflows/ci.yml` 只在 PR 时跑测试,push 不触发(不然每次推送两边各部署一遍)。开发机不在、又急着上线时,在 github.com 的 Actions → CI/CD → Run workflow:测试通过后 SSH 到服务器执行 `deploy/update-bg.sh <提交号>`(后台跑 `update.sh`),**服务器自己编**。这个提交要已经推到 origin(服务器从 origin 拉代码,没到时脚本会等最多约 100 秒)。
|
||||||
|
|
||||||
一次性配置(在本机执行,需要已登录的 `gh`):
|
一次性配置(在本机执行,需要已登录的 `gh`):
|
||||||
|
|
||||||
|
|||||||
Executable
+63
@@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 在服务器上执行:后台跑 deploy/update.sh,前台跟着输出,退出码和 update.sh 一样。
|
||||||
|
# 用法:deploy/update-bg.sh <提交号> [--prebuilt] 开始部署这个提交并跟着看
|
||||||
|
# deploy/update-bg.sh --follow <提交号> 接着看这个提交的部署(SSH 断线重连后用);
|
||||||
|
# 这个提交的部署还没开始过时退出码 75
|
||||||
|
#
|
||||||
|
# 为什么要后台跑:以前 update.sh 直接跑在 SSH 会话里,SSH 一断(开发机网络切换、代理重启)就跟着被杀。
|
||||||
|
# 2026-11-03 断在 docker compose up 中间:旧容器删了、新容器只创建没启动,网站停了 40 分钟。
|
||||||
|
# 现在 update.sh 在新会话里跑(setsid),SSH 断了它照样跑完,开发机重连后用 --follow 拿结果。
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# 包在函数里:update.sh 会 git reset,运行中替换本文件,bash 先读完函数再执行,不受影响
|
||||||
|
main() {
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
local state=/var/tmp/iot_sim-update
|
||||||
|
# state.sha:这次部署的提交 state.pid:后台进程 state.log:输出 state.exit:update.sh 的退出码(跑完才有)
|
||||||
|
|
||||||
|
if [[ ${1:-} == --follow ]]; then
|
||||||
|
local sha=${2:?用法:deploy/update-bg.sh --follow <提交号>}
|
||||||
|
if [[ $(cat $state.sha 2>/dev/null || true) != "$sha" ]]; then
|
||||||
|
echo "服务器上没有 $sha 的部署记录(还没开始)"
|
||||||
|
exit 75
|
||||||
|
fi
|
||||||
|
follow
|
||||||
|
fi
|
||||||
|
|
||||||
|
local sha=${1:?用法:deploy/update-bg.sh <提交号> [--prebuilt]}
|
||||||
|
|
||||||
|
# 上一次还在跑(比如它的 SSH 断了、又推了新的)就等它跑完,两次 compose up 不能同时跑
|
||||||
|
local old
|
||||||
|
old=$(cat $state.pid 2>/dev/null || true)
|
||||||
|
if [[ -n $old ]] && kill -0 "$old" 2>/dev/null; then
|
||||||
|
echo "== 上一次部署($(cat $state.sha 2>/dev/null))还在跑,等它结束"
|
||||||
|
while kill -0 "$old" 2>/dev/null; do sleep 2; done
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f $state.pid $state.exit
|
||||||
|
echo "$sha" >$state.sha
|
||||||
|
# 后台进程自己写 pid($$),保证记下的就是会写退出码的那个进程
|
||||||
|
setsid bash -c 'echo $$ >"$0.pid"; deploy/update.sh "$@"; echo $? >"$0.exit"' \
|
||||||
|
"$state" "$@" >$state.log 2>&1 </dev/null &
|
||||||
|
for _ in $(seq 50); do [[ -s $state.pid ]] && break; sleep 0.1; done
|
||||||
|
follow
|
||||||
|
}
|
||||||
|
|
||||||
|
follow() {
|
||||||
|
local pid code
|
||||||
|
pid=$(cat $state.pid 2>/dev/null || true)
|
||||||
|
if [[ -n $pid ]]; then
|
||||||
|
# update 进程结束后 tail 自己退出
|
||||||
|
tail -n +1 -f --pid="$pid" $state.log
|
||||||
|
else
|
||||||
|
cat $state.log 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
code=$(cat $state.exit 2>/dev/null || true)
|
||||||
|
if [[ -z $code ]]; then
|
||||||
|
echo "== 部署进程意外结束,没有留下退出码,看看 docker compose ps"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
exit "$code"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in new issue
Block a user