From b79fe60a56f7813a55d85871194011a4d63f0f57 Mon Sep 17 00:00:00 2001 From: shy3130 Date: Sun, 28 Jun 2026 16:18:46 +0800 Subject: [PATCH] =?UTF-8?q?ci(docker):=20=E4=BC=98=E5=8C=96=E9=95=9C?= =?UTF-8?q?=E5=83=8F=E6=9E=84=E5=BB=BA=E5=B7=A5=E4=BD=9C=E6=B5=81=20+=20?= =?UTF-8?q?=E6=B8=85=E7=90=86=E6=9C=AA=E5=90=AF=E7=94=A8=E7=9A=84=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E8=87=AA=E5=A2=9E=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 #22 (Docker 镜像构建工作流) 基础上的三点优化: 1. CI 关闭国内镜像源(USE_CN_MIRROR=0): GitHub runner 在海外, 让构建直连官方 npm/PyPI 反而更快更稳; 本地/国内手动 docker build 仍走 Dockerfile 默认的 CN 镜像。 2. 新增 .dockerignore: 排除 .git/data/node_modules/docs/screenshots 等 非构建必需文件, 减小上下文体积、加速传输。 3. 叠加 tag 触发 + build-push-action v5→v6: 打 v* tag 时额外产出 带版本号镜像(如 :v0.1.62), 便于固定回溯。 清理: - 删除 scripts/bump_version.py (prepare-commit-msg hook 从未实际安装, 属于死代码; 发版版本号改为手动维护, 与方案 B 一致)。 - 同步修正 backend/app/api/data.py 中对该脚本的过时引用注释。 --- .dockerignore | 57 ++++++++++++++++++++++ .github/workflows/docker.yml | 13 ++++- backend/app/api/data.py | 2 +- scripts/bump_version.py | 94 ------------------------------------ 4 files changed, 70 insertions(+), 96 deletions(-) create mode 100644 .dockerignore delete mode 100644 scripts/bump_version.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d71403f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,57 @@ +# 构建 Docker 镜像时排除的文件 / 目录。 +# 与 .gitignore 思路一致: 只把 Dockerfile 真正需要的送进构建上下文, +# 减小上下文体积、加速上传、避免缓存误伤。 +# 注意: .dockerignore 不识别 .gitignore 的取反(!)语法, 这里按白名单思路直接列举排除项。 + +# ===== 版本控制 / CI ===== +.git +.github +.gitignore + +# ===== 用户运行时数据(绝不应进镜像) ===== +data +backend/data + +# ===== 前端构建产物 / 依赖(镜像内会重新 build, 带进来反而污染) ===== +frontend/node_modules +frontend/dist +backend/static +backend/app/static + +# ===== Python 缓存 / 虚拟环境 ===== +**/__pycache__ +**/*.py[cod] +**/.pytest_cache +**/.mypy_cache +**/.ruff_cache +**/.venv +**/venv + +# ===== IDE / 系统 ===== +.idea +.vscode +.DS_Store +**/*.swp + +# ===== 文档 / 截图(镜像不需要; README.md 除外, Dockerfile 显式 COPY 了它) ===== +docs +screenshots + +# ===== 桌面客户端打包(PyInstaller 专用, 与 Docker 部署无关) ===== +packaging +backend/dist +backend/build + +# ===== 开发脚本(本地启服务用, 镜像不用) ===== +dev.sh +dev.ps1 +scripts + +# ===== 密钥(防止误打进公开镜像) ===== +.env +.env.* +backend/.env + +# ===== 其他构建产物 ===== +**/node_modules +*.log diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f528352..4709596 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,8 +1,13 @@ name: Build and Push Docker Image +# 触发: +# - push 到 main: 覆盖刷新 :latest + :sha(每次提交可追溯) +# - 打 v* tag: 额外产出带版本号的镜像(如 :v0.1.43), 便于固定回溯 +# - 手动: 网页 Actions → Run workflow on: push: branches: [main] + tags: ['v*'] workflow_dispatch: env: @@ -42,13 +47,19 @@ jobs: tags: | type=raw,value=latest type=sha,prefix= + type=match,pattern=v*,group=0 - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . push: true platforms: linux/amd64,linux/arm64 + # GitHub runner 在海外, 关掉国内镜像源(USE_CN_MIRROR=0) + # 让构建直连官方 npm/PyPI, 反而更快更稳; + # 本地/国内手动 docker build 仍走 Dockerfile 默认的 CN 镜像。 + build-args: | + USE_CN_MIRROR=0 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha diff --git a/backend/app/api/data.py b/backend/app/api/data.py index 2700d09..cbe72d3 100644 --- a/backend/app/api/data.py +++ b/backend/app/api/data.py @@ -846,7 +846,7 @@ def get_version(request: Request) -> dict: """ from app import __version__ - # 1. 优先用 app.__version__ (开发期 bump_version.py 写入, 打包期由 PyInstaller 注入) + # 1. 优先用 app.__version__ (唯一权威版本, 打包期由 PyInstaller 注入) if __version__: v = __version__.strip() return {"version": v if v.startswith("v") else f"v{v}"} diff --git a/scripts/bump_version.py b/scripts/bump_version.py deleted file mode 100644 index cd9d161..0000000 --- a/scripts/bump_version.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python3 -"""版本号自增脚本 — 由 git prepare-commit-msg hook 调用。 - -读取当前版本, +0.0.1, 写回 frontend/package.json 和 backend/pyproject.toml。 -跳过条件(避免 merge/rebase/amend 误触发): - - 非 master/main 分支(可选, 当前不限制) - - commit message 以 merge/squash/rebase 开头 - - 环境变量 SKIP_VERSION_BUMP=1 -""" -from __future__ import annotations - -import json -import re -import sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parent.parent -PKG = ROOT / "frontend" / "package.json" -PYPROJECT = ROOT / "backend" / "pyproject.toml" -APP_INIT = ROOT / "backend" / "app" / "__init__.py" # __version__ 写在这里, health 接口读它 - - -def parse_version(v: str) -> tuple[int, int, int]: - parts = v.strip().split(".") - while len(parts) < 3: - parts.append("0") - return int(parts[0]), int(parts[1]), int(parts[2]) - - -def bump(v: str) -> str: - major, minor, patch = parse_version(v) - return f"{major}.{minor}.{patch + 1}" - - -def read_pkg_version() -> str: - data = json.loads(PKG.read_text(encoding="utf-8")) - return data["version"] - - -def write_pkg_version(v: str) -> None: - data = json.loads(PKG.read_text(encoding="utf-8")) - data["version"] = v - PKG.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") - - -def read_pyproject_version() -> str: - text = PYPROJECT.read_text(encoding="utf-8") - m = re.search(r'^version\s*=\s*"([^"]+)"', text, re.MULTILINE) - return m.group(1) if m else "0.0.0" - - -def write_pyproject_version(v: str) -> None: - text = PYPROJECT.read_text(encoding="utf-8") - text = re.sub(r'^version\s*=\s*"[^"]+"', f'version = "{v}"', text, count=1, flags=re.MULTILINE) - PYPROJECT.write_text(text, encoding="utf-8") - - -def write_app_init_version(v: str) -> None: - """更新 backend/app/__init__.py 的 __version__ (health 接口读这里)。""" - text = APP_INIT.read_text(encoding="utf-8") - text = re.sub( - r'^(__version__\s*=\s*)"[^"]+"', - rf'\1"{v}"', - text, - count=1, - flags=re.MULTILINE, - ) - APP_INIT.write_text(text, encoding="utf-8") - - -def main() -> int: - # 环境变量跳过 - if __import__("os").environ.get("SKIP_VERSION_BUMP") == "1": - return 0 - - cur = read_pkg_version() - new = bump(cur) - - # 三个文件版本可能不一致, 统一用 pkg 的为准 - write_pkg_version(new) - write_pyproject_version(new) - write_app_init_version(new) - - # 暂存版本文件改动(并入本次 commit) - import subprocess - subprocess.run(["git", "add", str(PKG), str(PYPROJECT), str(APP_INIT)], check=True, cwd=str(ROOT)) - - # 输出新版本号供 hook 读用 - print(f"[bump] {cur} -> {new}") - return 0 - - -if __name__ == "__main__": - sys.exit(main())