Files
OJ2/.github/workflows/deploy.yml
yuetsh 64cb7b62ef
Some checks failed
Deploy / deploy (push) Has been cancelled
ci(部署): 手动触发可放行破坏性迁移,push 触发永远不放行
deploy.sh 现在会在起栈前跑迁移,但 CI 没法传 OJ2_ALLOW_DESTRUCTIVE,
于是任何破坏性迁移都会让自动部署一直红着,且没有出口。

给 workflow_dispatch 加一个 allow_destructive 布尔 input,经 env: 传到
ssh 命令里。push 触发拿不到 inputs,值恒为空 —— 也就是说**自动部署永远
不会执行 DROP TABLE 之类的迁移**,只会停在闸门上把工作流判红。这是有意的:
那种改动得有人先确认备份,不该由一次 git push 顺带完成。

日常的加索引 / 加列属于非破坏性,CI 部署会照常自动执行,无需干预。

留了一条注释标明必须用 `inputs.` 而不是 `github.event.inputs.`:后者会把
布尔字符串化,没勾选时拿到字符串 "false",那是非空字符串、在 `&&` 里为真,
等于每次手动部署都无条件放行。

验证:本机 YAML 解析通过,确认 inputs 定义挂在 workflow_dispatch 下。
表达式三种情况的语义(勾选 → '1'、未勾选 → ''、push 事件 inputs 为空 → '')
是 GitHub 表达式的既定行为,要真的 push 到 github 才跑得起来。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 08:38:47 -06:00

128 lines
5.9 KiB
YAML
Raw Permalink 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.
# 在 runner 上编好产物 → rsync 到服务器 → 服务器上跑 docker/deploy.sh 起栈。
#
# 为什么产物在这里编:服务器性能差,镜像 builder 阶段1238 个包的 bun install +
# bun compile + vite build首次约 5 分钟,光前端就 160s。runner 编完传过去,
# 服务器那边的镜像构建只剩几条 COPY。
#
# 服务器自己编的能力**没有砍掉**docker/deploy.sh 不带 --prebuilt 就是原来的
# 行为ARTIFACTS=build只要机器上有 docker 就能手动部署,不依赖这个工作流。
#
# 触发的是 push 到 **github** 这个 remoteorigin 是 git.xuyue.cc平时那次
# push 不会触发):
#
# git push github main
#
# 仓库 Settings → Secrets 需要两个:
# HOST 服务器地址
# KEY 能以 root 登录该服务器的私钥(完整 PEM含首尾 BEGIN/END 行)
name: Deploy
on:
push:
branches:
- main
# 手动触发时可以放行破坏性迁移。push 触发拿不到这个 input值恒为空 ——
# 也就是说**自动部署永远不会执行 DROP TABLE 之类的迁移**,只会停在闸门上把
# 工作流判红。这是有意的:那种改动得有人先确认备份。
workflow_dispatch:
inputs:
allow_destructive:
description: "放行破坏性迁移DROP TABLE / DROP COLUMN / ALTER TYPE …)。勾之前先确认已备份数据库。"
type: boolean
default: false
permissions:
contents: read
# 两次 push 挨得近时,别让两个 deploy.sh 在服务器上同时起栈。
concurrency:
group: deploy
cancel-in-progress: false
env:
REMOTE_PORT: 22
REMOTE_DIR: /root/OJDeploy/OJ2
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
# 依赖装到 ~/.bun/install/cache命中的话 install 只剩几秒。
# v6 起跑在 node24 上v4 还是 node20GitHub 正在退役 node20 runtime
# 和上面两个 action 一致。v5+ 要求 runner >= 2.327.1ubuntu-latest 是
# GitHub 托管的、始终满足;换自建 runner 的话要留意这条。
- uses: actions/cache@v6
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }}
restore-keys: bun-${{ runner.os }}-
- name: 装依赖
run: bun install --frozen-lockfile
# 单二进制,--target=bun-linux-x64runner 就是 linux-x64和服务器一致
# 输出到 dist/oj2-api正是 Dockerfile 里 artifacts-prebuilt 阶段要的位置。
- name: 编后端
run: bun run --filter '@oj2/api' build
# 默认 production 模式,读 apps/web/.env.production在版本库里
- name: 编前端
run: cd apps/web && bun run build
- name: 配置 SSH
run: |
mkdir -p ~/.ssh && chmod 700 ~/.ssh
printf '%s\n' "${{ secrets.KEY }}" > ~/.ssh/id_deploy
chmod 600 ~/.ssh/id_deploy
ssh-keyscan -p "$REMOTE_PORT" -H "${{ secrets.HOST }}" >> ~/.ssh/known_hosts 2>/dev/null
# 源码和产物一起传。dist/oj2-api 有 77MB但 rsync 对已存在的文件做增量,
# 重编后大部分bun 运行时那段)字节不变,实际传输量远小于这个数。
#
# --delete 的排除项少一个就会出事,逐条说明:
# docker/.env 服务器上唯一一份,不在版本库里(.gitignore 排掉了 .env*)。
# 删了下一步 deploy.sh 自检就直接 die。
# data/ 测试点、题面图片、学生上传的文件,还有「自带数据」形态下的
# 整个 postgres 数据目录。删了就是删库。
# node_modules 服务器上根本不需要prebuilt 形态下连编都不编),传过去纯浪费。
# .git checkout 出来的 .git 没有任何用处。
#
# --no-owner --no-group 别删:`-a` 展开含 `-o -g`(保留属主/属组),而接收端是
# rootrsync 会把**源端的 uid 真写上去**——GitHub 托管 runner 的 runner 用户是
# uid 1001于是服务器上的工作区目录变成 1001 所有。而 `.git` 被 --exclude 掉,
# 还是当初手动 clone 时的 root。两者对不上之后在服务器上跑任何 git 命令都会被
# 「检测到可疑的仓库所有权」挡下git 2.35.2 起的 safe.directory 检查)。
- name: 同步到服务器
run: |
rsync -az --no-owner --no-group --delete \
-e "ssh -i ~/.ssh/id_deploy -p $REMOTE_PORT" \
--exclude node_modules \
--exclude .git \
--exclude data \
--exclude 'docker/.env*' \
./ "root@${{ secrets.HOST }}:$REMOTE_DIR/"
# deploy.sh 自检不过 / 迁移被拦 / 容器没起来 / 冒烟发现题目数是 0都会非零退出
# 这一步跟着红,日志里能看到具体是哪一条。
#
# 迁移在 deploy.sh 里排在起栈之前:非破坏性的(加索引、加列)自动执行;
# 破坏性的会被拦下并退出 4除非手动触发时勾了 allow_destructive。
# 走 env: 传而不是把 ${{ }} 直接拼进命令串 —— 值虽然是 GitHub 约束过的布尔,
# 但拼字符串进 shell 的写法不该在这个仓库里留下先例。
#
# ⚠️ 必须用 `inputs.`**不能换成 `github.event.inputs.`**。后者把布尔
# 字符串化,没勾选时拿到的是字符串 "false" —— 那是个非空字符串,在
# `&&` 里为真,等于每次手动部署都无条件放行破坏性迁移。
- name: 起栈
env:
ALLOW_DESTRUCTIVE: ${{ inputs.allow_destructive && '1' || '' }}
run: |
ssh -i ~/.ssh/id_deploy -p "$REMOTE_PORT" "root@${{ secrets.HOST }}" \
"cd $REMOTE_DIR && OJ2_ALLOW_DESTRUCTIVE='$ALLOW_DESTRUCTIVE' docker/deploy.sh --prebuilt"