diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6eb4138..3aaad50 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -3,14 +3,14 @@ name: Deploy WeReadBoard on: push: branches: - - dev + - master workflow_dispatch: jobs: deploy: runs-on: ubuntu-latest concurrency: - group: deploy-dev + group: deploy-master cancel-in-progress: true steps: @@ -27,7 +27,7 @@ jobs: SERVER_USER: ${{ secrets.SERVER_USER }} DEPLOY_PATH: ${{ vars.DEPLOY_PATH }} SERVICE_NAME: ${{ vars.SERVICE_NAME }} - DEPLOY_BRANCH: ${{ github.ref_name }} + DEPLOY_BRANCH: master run: | if [ -z "$SERVER_PORT" ]; then SERVER_PORT="22" diff --git a/README.md b/README.md index d9363fd..6c42184 100644 --- a/README.md +++ b/README.md @@ -67,10 +67,10 @@ PORT=8080 ./start_dashboard.sh # 或指定端口 服务器部署使用根目录的 `deploy.sh`: ```bash -DEPLOY_BRANCH=dev ./deploy.sh +DEPLOY_BRANCH=master ./deploy.sh ``` -脚本会拉取指定分支,自动创建 `.venv`,安装 `requirements.txt`,执行数据库迁移,然后重启 `weread.service`。GitHub Actions 配置在 `.github/workflows/deploy.yml`,默认监听 `dev` 分支。 +脚本只允许拉取 `master` 分支,随后自动创建 `.venv`、安装 `requirements.txt`、执行数据库迁移并重启 `weread.service`。GitHub Actions 配置在 `.github/workflows/deploy.yml`,只监听 `master` 分支,详细配置见 [master 自动部署](docs/deploy-master.md)。 运行端口、Python 环境、HTTPS 证书和接口故障的诊断方法见 [日常维护与故障排查](docs/troubleshooting.md)。 diff --git a/deploy.sh b/deploy.sh index 5bbf0df..31b2da3 100755 --- a/deploy.sh +++ b/deploy.sh @@ -4,19 +4,15 @@ set -euo pipefail PROJECT_DIR="${PROJECT_DIR:-$(cd "$(dirname "$0")" && pwd)}" REMOTE="${REMOTE:-origin}" -BRANCH="${DEPLOY_BRANCH:-${GITHUB_REF_NAME:-}}" +BRANCH="${DEPLOY_BRANCH:-master}" SERVICE_NAME="${SERVICE_NAME:-weread.service}" VENV_DIR="${VENV_DIR:-$PROJECT_DIR/.venv}" ENV_FILE="${ENV_FILE:-$PROJECT_DIR/.env}" cd "$PROJECT_DIR" -if [ -z "$BRANCH" ]; then - BRANCH="$(git branch --show-current)" -fi - -if [ -z "$BRANCH" ]; then - echo "未检测到部署分支,请设置 DEPLOY_BRANCH" +if [ "$BRANCH" != "master" ]; then + echo "拒绝部署:当前只允许部署 master 分支" exit 1 fi diff --git a/docs/code-design.md b/docs/code-design.md index ff99ee8..466e7c8 100644 --- a/docs/code-design.md +++ b/docs/code-design.md @@ -43,11 +43,11 @@ ## 部署流程 -GitHub Actions 的 `.github/workflows/deploy.yml` 监听 `dev` 分支。分支 push 后,Actions 使用仓库 Secrets 中的服务器地址、用户和 SSH 私钥登录服务器,进入 `/opt/weread-board/WeReadBoard` 后调用 `deploy.sh`。 +GitHub Actions 的 `.github/workflows/deploy.yml` 只监听 `master` 分支。分支 push 后,Actions 使用仓库 Secrets 中的服务器地址、用户和 SSH 私钥登录服务器,进入 `/opt/weread-board/WeReadBoard` 后调用 `deploy.sh`。手动触发也固定部署 `master`,部署脚本会拒绝其他分支。 -`deploy.sh` 以当前触发分支为准拉取代码,再检查项目 `.venv`。如果 `.venv/bin/python` 不存在,脚本使用服务器上的 `python3` 或 `python` 创建虚拟环境;随后用 `.venv/bin/python -m pip install -r requirements.txt` 安装依赖,执行 `migrations/001_reading_goals.sql`,最后重启 `weread.service`。 +`deploy.sh` 固定拉取 `master`,收到其他部署分支参数时直接退出,再检查项目 `.venv`。如果 `.venv/bin/python` 不存在,脚本使用服务器上的 `python3` 或 `python` 创建虚拟环境;随后用 `.venv/bin/python -m pip install -r requirements.txt` 安装依赖,执行 `migrations/001_reading_goals.sql`,最后重启 `weread.service`。 -例:用户把代码推送到 `dev` 分支,服务器原来没有 `.venv`。部署脚本会在 `/opt/weread-board/WeReadBoard/.venv` 创建环境,安装 Flask 和 PyMySQL,确认阅读目标表存在,再让 systemd 重新用 `.venv/bin/python app.py` 启动服务。 +例:用户将已验收代码合并并推送到 `master`,服务器原来没有 `.venv`。部署脚本会在 `/opt/weread-board/WeReadBoard/.venv` 创建环境,安装 Flask 和 PyMySQL,确认阅读目标表存在,再让 systemd 重新用 `.venv/bin/python app.py` 启动服务。仅推送 `dev` 不会触发部署。 ## 示例与真实接口的边界 diff --git a/docs/deploy-dev.md b/docs/deploy-master.md similarity index 72% rename from docs/deploy-dev.md rename to docs/deploy-master.md index 7d6b075..37bf9a8 100644 --- a/docs/deploy-dev.md +++ b/docs/deploy-master.md @@ -1,6 +1,6 @@ -# dev 自动部署 +# master 自动部署 -GitHub Actions workflow 位于 `.github/workflows/deploy.yml`。每次 `dev` 分支 push 后,Actions 会通过 SSH 登录服务器,在部署目录执行根目录 `deploy.sh`,拉取最新 `dev`、安装依赖、执行阅读目标表迁移并重启服务。 +GitHub Actions workflow 位于 `.github/workflows/deploy.yml`。每次 `master` 分支 push 后,Actions 会通过 SSH 登录服务器,在部署目录执行根目录 `deploy.sh`,拉取最新 `master`、安装依赖、执行阅读目标表迁移并重启服务。手动触发时也固定部署 `master`,不能选择其他分支发布。 ## GitHub 配置 @@ -20,7 +20,7 @@ GitHub Actions workflow 位于 `.github/workflows/deploy.yml`。每次 `dev` 分 ## 服务器要求 -服务器部署目录需要已经是本仓库的 Git checkout,并且能访问 `origin/dev`。运行服务的环境变量仍按现有应用配置,例如 `WEREAD_API_KEY`、`WEREAD_DB_HOST`、`WEREAD_DB_PORT`、`WEREAD_DB_USER`、`WEREAD_DB_PASSWORD` 和 `WEREAD_DB_NAME`。 +服务器部署目录需要已经是本仓库的 Git checkout,并且能访问 `origin/master`。运行服务的环境变量仍按现有应用配置,例如 `WEREAD_API_KEY`、`WEREAD_DB_HOST`、`WEREAD_DB_PORT`、`WEREAD_DB_USER`、`WEREAD_DB_PASSWORD` 和 `WEREAD_DB_NAME`。 如果这些变量放在项目目录的 `.env` 文件中,部署脚本会先加载 `.env`,再执行数据库迁移。SSH 用户需要能执行 `systemctl restart <服务名>`;非 root 用户需要配置免密 `sudo systemctl restart/status <服务名>`。 @@ -28,8 +28,8 @@ GitHub Actions workflow 位于 `.github/workflows/deploy.yml`。每次 `dev` 分 ```bash git fetch origin -git checkout dev -git pull --ff-only origin dev +git checkout master +git pull --ff-only origin master python3 -m venv .venv # 仅 .venv 不存在时执行 .venv/bin/python -m pip install -r requirements.txt # 读取 reading_api.MYSQL_CONFIG 后执行 migrations/001_reading_goals.sql @@ -38,6 +38,8 @@ sudo systemctl restart "$SERVICE_NAME" 其中迁移实际通过 `reading_api.MYSQL_CONFIG` 读取数据库配置并执行 SQL 文件;SQL 使用 `CREATE TABLE IF NOT EXISTS`,可重复执行。 +脚本默认部署 `master`,显式传入其他 `DEPLOY_BRANCH` 时会在拉取代码、安装依赖和重启服务之前退出。 + ## systemd 示例 ```ini diff --git a/docs/technical-guide.md b/docs/technical-guide.md index 1e4fbd8..d7773b2 100644 --- a/docs/technical-guide.md +++ b/docs/technical-guide.md @@ -100,7 +100,7 @@ SERVICE_NAME=weread.service VENV_DIR=/opt/weread-board/WeReadBoard/.venv ``` -GitHub Actions 文件位于 `.github/workflows/deploy.yml`,默认在 `dev` 分支 push 后触发,也支持手动触发。仓库需要在 `Settings -> Secrets and variables -> Actions -> Secrets` 配置: +GitHub Actions 文件位于 `.github/workflows/deploy.yml`,只在 `master` 分支 push 后触发,也支持手动触发;手动触发同样固定部署 `master`。仓库需要在 `Settings -> Secrets and variables -> Actions -> Secrets` 配置: | Secret | 说明 | |---|---| @@ -120,7 +120,7 @@ GitHub Actions 文件位于 `.github/workflows/deploy.yml`,默认在 `dev` 分 ```sh cd /opt/weread-board/WeReadBoard -DEPLOY_BRANCH=dev ./deploy.sh +DEPLOY_BRANCH=master ./deploy.sh ``` `deploy.sh` 会优先读取项目根目录 `.env`;没有 `.env` 时,会读取 `weread.service` 当前 `Environment=` 配置。按截图中的 service 写法也能拿到端口、API Key 和数据库配置。若以后改用普通部署用户,需要允许该用户免密执行 `systemctl restart weread.service`。 @@ -148,8 +148,8 @@ node --check static/js/weapi.js 本次样式收紧覆盖正式看板的概览、书架和阅读目标页。顶栏、标题区、筛选区、统计卡、书架网格、右侧目标摘要、目标介绍区和目标卡片均使用覆盖规则压缩占位;书架在窄屏固定两列,避免横向溢出。本地真实数据验收覆盖 1280 px 桌面和 390 px 手机宽度:桌面顶栏 58 px,统计卡从约 200 px 开始,书架一行 7 本,目标页介绍区约 67 px 高,目标卡约 317 px 宽;手机顶栏 56 px,书架两列,目标卡约 298 px 宽,概览、书架和目标页均未出现横向溢出。 -## dev 自动部署 +## master 自动部署 -`dev` 分支 push 后由 `.github/workflows/deploy.yml` 触发部署。Actions 通过仓库 Secrets 登录服务器,在 `DEPLOY_PATH` 指定的目录先拉取 `origin/dev`,再执行根目录 `deploy.sh`,完成加载可选 `.env`、安装 Python 依赖、执行阅读目标表迁移和重启 systemd 服务。详细配置见 `docs/deploy-dev.md`。 +`master` 分支 push 后由 `.github/workflows/deploy.yml` 触发部署。Actions 通过仓库 Secrets 登录服务器,在 `DEPLOY_PATH` 指定的目录先拉取 `origin/master`,再执行根目录 `deploy.sh`,完成加载可选 `.env`、安装 Python 依赖、执行阅读目标表迁移和重启 systemd 服务。`dev` 和其他分支不会触发部署,部署脚本也会拒绝非 `master` 分支。详细配置见 [master 自动部署](deploy-master.md)。 本次数据库验收已在 8080 正式看板完成:页面新建目标后刷新仍可读取,编辑后再次刷新仍可读取,并已清理临时验收目标。读取目标列表未完成时,新建和编辑入口只显示目标页的加载或错误状态,不会打开保存表单。 diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index e704cab..7b5ee4c 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -10,6 +10,7 @@ | 当前 API 前缀 | `/api/reading/*` | | 兼容 API 前缀 | `/api/v2/*`,仅用于旧客户端过渡 | | 项目 Python | 优先使用项目根目录 `.venv/bin/python` | +| 自动部署分支 | 仅 `master`;推送或手动触发均不部署其他分支 | | 服务器服务 | `weread.service`,端口以服务中的 `PORT` 为准 | | 服务器目录 | `/opt/weread-board/WeReadBoard` | @@ -62,6 +63,10 @@ curl --noproxy '*' -i 'http://127.0.0.1:8080/api/reading/dashboard?period=week' sudo journalctl -u weread.service -n 100 --no-pager ``` +### 检查部署分支 + +自动部署只允许 `master`。如果 Actions 没有启动,先确认提交是否已经合并并推送到 `master`;仅推送 `dev` 不会部署。服务器部署日志中的拉取目标也必须是 `origin/master`,出现其他分支时应停止发布并检查工作流与 `deploy.sh`。 + ## 2026-09-14:接口调整后页面请求失败 ### 现象 diff --git a/tests/deploy_master_only.test.sh b/tests/deploy_master_only.test.sh new file mode 100755 index 0000000..ea6e9a1 --- /dev/null +++ b/tests/deploy_master_only.test.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +WORKFLOW="$ROOT_DIR/.github/workflows/deploy.yml" + +if ! grep -q '^ - master$' "$WORKFLOW"; then + echo "部署工作流尚未监听 master" + exit 1 +fi +if grep -q '^ - dev$' "$WORKFLOW"; then + echo "部署工作流仍监听 dev" + exit 1 +fi +if ! grep -q '^ group: deploy-master$' "$WORKFLOW"; then + echo "部署并发组尚未切换到 master" + exit 1 +fi +if ! grep -q '^ DEPLOY_BRANCH: master$' "$WORKFLOW"; then + echo "手动部署分支尚未固定为 master" + exit 1 +fi + +TEST_DIR="$(mktemp -d)" +trap 'rm -rf "$TEST_DIR"' EXIT +mkdir -p "$TEST_DIR/project" "$TEST_DIR/bin" +printf '#!/usr/bin/env bash\nexit 0\n' > "$TEST_DIR/bin/python3" +chmod +x "$TEST_DIR/bin/python3" + +set +e +OUTPUT="$( + PATH="$TEST_DIR/bin" \ + PROJECT_DIR="$TEST_DIR/project" \ + VENV_DIR="$TEST_DIR/project/.venv" \ + DEPLOY_BRANCH=dev \ + SKIP_GIT_PULL=1 \ + /bin/bash "$ROOT_DIR/deploy.sh" 2>&1 +)" +STATUS=$? +set -e + +if [ "$STATUS" -eq 0 ] || [[ "$OUTPUT" != *"只允许部署 master"* ]]; then + echo "deploy.sh 没有拒绝 dev 分支" + exit 1 +fi + +echo "master-only 部署约束验证通过"