Files
FinDataHub/.github/workflows/ci.yml
T
FinDataHub ad3dbdb9dc ci: 新增 Windows job 与 MySQL service
## Windows 跨平台回归(观察期,不阻塞)

目的:验证 paths.py / process_utils.py 在 win32 上真的可用,而不是"写完就算"。
这两个模块已接线 4 个脚本,一旦 Windows 分支写错,表现是采集器静默不启动
或 PID 检测失效——本地 macOS 上完全看不出来。

**范围有意收窄:不装 akshare**。它传递依赖 py_mini_racer(62MB V8 预编译),
Windows 上是否有 3.12 wheel 未验证,属迁移计划列明的唯一硬阻塞点。
在验证它之前塞进 CI 只会让 job 长期飘红、失去信号价值。

覆盖:check_python_version --strict → compileall → test_paths_process_utils
(32 条,核心)→ Scripts/bin/tests(252 条,此前只在 POSIX 跑过)→ core + client。

continue-on-error: true + 注释写明转正条件(连续两次全绿后删掉)。
首次接跨平台 CI 若直接阻塞,一次环境差异就把主线挡死,反而逼人绕过它。

已提前修掉一处确定会失败的用例:test_newsflash_collect 的 SIGTERM 留痕测试。
Windows 的 send_signal(SIGTERM) 实际走 TerminateProcess,不触发子进程里的
Python handler,断言必然失败。已加 skipif 并注明该能力本身是 POSIX 专属。

## MySQL service(集成测试转阻塞)

python job 增加 services: mysql(mysql:8.0),集成测试成为阻塞性步骤。

三个必须处理的差异:
1. 认证插件:MySQL 8 默认 caching_sha2_password,首次连接走 full path
   需 RSA → 装 cryptography,另加 --default-authentication-plugin 双保险
   (注意 8.4 起该参数已移除,升镜像需同步改)。
   ⚠️ 本机某些 venv 没装 cryptography 也能连——那是认证缓存命中 fast path,
   不代表 CI 也需要。只看本地结果会误判依赖冗余。
2. 版本:CI 8.0 vs 生产 26.7。事务/回滚语义一致(InnoDB),套件只用
   CREATE TABLE + UNIQUE KEY,无版本敏感语法。
3. YAML 的 >- 折叠块内不能写注释:'#' 会被当成内容传给 docker,容器启动即失败。
   注释必须置于 options: 之前(已用 assert '#' not in opts 验证)。

两级排除避免重复跑:python job 单元测试步骤与 windows job 均加
--ignore=.../integration。
2026-10-26 20:56:01 +08:00

207 lines
9.3 KiB
YAML
Raw 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.
name: CI
on:
push:
branches: [main, master]
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# ── Python 侧:版本一致性 + 语法 + 单元测试 ──────────────────────
python:
name: Python 3.12 一致性 / 语法 / 测试
runs-on: ubuntu-latest
# 真实 MySQL:给 replace 的事务语义做真库验证(fake 游标验不出回滚与半成品)
#
# 版本差异需知悉:CI 用 MySQL 8.0,生产是 26.7。事务/回滚语义一致(同为 InnoDB),
# 但 DDL 默认值与部分语法可能不同。集成套件只用 CREATE TABLE + UNIQUE KEY,
# 不含版本敏感语法;若将来要测分区表、生成列等特性,须先在此对齐版本。
#
# 密码为临时容器专用明文:service container 不暴露到公网,且随 runner 销毁。
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: ci_root_pw_test_only
ports:
- 3306:3306
# 注:--default-authentication-plugin 仅为双保险(配合 cryptography)。
# MySQL 8.4 起该参数已移除、改用 --authentication-policy,
# 将来升级镜像版本需同步调整,否则容器启动即失败。
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -pci_root_pw_test_only"
--health-interval=10s
--health-timeout=5s
--health-retries=12
--default-authentication-plugin=mysql_native_password
steps:
- uses: actions/checkout@v4
# 版本来源唯一:读取仓库根目录 .python-version,
# 避免 CI 里再写死一份版本号造成漂移
- uses: actions/setup-python@v5
with:
python-version-file: '.python-version'
- name: 显示版本
run: python3 --version
- name: 版本一致性检查(声明 / venv / 硬编码引用)
run: python3 Scripts/bin/check_python_version.py --strict
- name: 语法编译检查
run: |
python3 -m compileall -q \
Scripts/bin \
Services/findatahub-core/src \
Services/kuaixun-service/app \
Services/kuaixun-service/extend \
Services/findatahub-client/src \
Services/tushare_fetcher/src \
Services/akshare_fetcher/src
- name: 安装测试依赖
# cryptography 是 pymysql 走 caching_sha2_password 的 **full path** 认证时
# 必需的(需要 RSA 公钥交换)。缺了会报 "cryptography package is required"。
#
# 注意一个容易误判的现象:本机某些 venv 没装 cryptography 也能连上库——
# 那是因为该 MySQL 实例的认证信息已被缓存,握手走了 fast path。
# CI 里是**全新容器、首次连接**,必然走 full path,所以这里必须装。
run: python3 -m pip install --quiet pytest pytest-asyncio cryptography
- name: 安装本地包(含各服务)
# findatahub-core 不在 PyPI 上,必须先本地安装;
# findatahub-client 的测试会 import 它。
# 用非 editable 安装:在本机 uv+setuptools 组合下 build_editable 会 EEXIST 失败,
# 非 editable 在各平台都稳。
run: |
python3 -m pip install --quiet ./Services/findatahub-core
python3 -m pip install --quiet ./Services/findatahub-client
python3 -m pip install --quiet -e ./Services/findatahub-core --no-build-isolation 2>/dev/null || true
- name: 统一测试入口自检
# 只验证解释器探测与套件声明是否可用,不跑测试(成本近乎 0)。
# 目的:防止 Scripts/bin/run_all_tests.py 随目录调整悄悄失效——
# 脚本腐烂时这里会以退出码 2 明确报错,而不是等到有人在本地踩坑。
run: python3 Scripts/bin/run_all_tests.py --list
- name: 运行单元测试(核心与采集器,纯离线)
# 这些用例全部为 mock 驱动、不连库,故设为阻塞性。
# integration 子目录单独跑(见下一步),此处排除以免重复。
run: |
python3 -m pytest Services/findatahub-core/tests -q --ignore=Services/findatahub-core/tests/integration
python3 -m pytest Services/findatahub-client/tests -q
python3 -m pytest Services/tushare_fetcher/tests/unit -q
python3 -m pytest Services/akshare_fetcher/tests -q
python3 -m pytest Scripts/bin/tests -q
- name: 真实库集成测试(replace 事务语义)
# 验证 fake 游标验不了的三件事:
# 1. 事务是否真的回滚(fake 的 rollback 只是置标志位)
# 2. 跨 chunk 失败是否留下「半成品」(fake 不会真提交,永远复现不出来)
# 3. 熔断时表是否真的没动
# 测试自建 fdh_it_replace_<随机> 空库并 DROP,不碰 service 里的任何现有库。
# 曾做证伪:把实现改回「每 chunk 提交」,本套件会报
# 「回滚不完整:表内行数既不是旧值」,证明用例有效。
env:
MYSQL_HOST: 127.0.0.1
MYSQL_PORT: "3306"
MYSQL_USER: root
MYSQL_PASSWORD: ci_root_pw_test_only
run: python3 -m pytest Services/findatahub-core/tests/integration -q
- name: 运行单元测试(kuaixun-service,部分用例可能依赖本机服务)
# 该套件含需要 MySQL / Ollama 的用例,暂不阻塞 CI
continue-on-error: true
run: python3 -m pytest Services/kuaixun-service/tests -q
# ── Swift 侧:编译验证 ────────────────────────────────────────
swift:
name: Swift 编译(macOS)
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: 显示工具链
run: swift --version
- name: 编译 release
# 本机 SPM manifest 沙箱(sandbox-exec)不可用,必须禁用
run: swift build -c release --disable-sandbox
- name: Shell 脚本语法检查
run: |
for f in Scripts/build-app.sh Scripts/bin/*.sh; do
bash -n "$f" && echo "ok: $f"
done
# ── Windows 侧:跨平台抽象层回归 ──────────────────────────────
#
# 目的:验证 `Scripts/bin/paths.py` 与 `process_utils.py` 在 win32 上真的可用,
# 而不是"写完就算"。这两个模块已接线 4 个脚本,一旦 Windows 分支写错,
# 表现是采集器静默不启动或 PID 检测失效——本地 macOS 上完全看不出来。
#
# 范围有意收窄:**不装 akshare**。它传递依赖 py_mini_racer(62MB V8 预编译二进制),
# Windows 上是否有 3.12 wheel 未验证,属已知硬阻塞点;
# 因此 Windows 侧只跑不依赖它的套件。tushare / akshare / backend 待后续单独验证。
windows:
name: Windows 跨平台回归
runs-on: windows-latest
# 观察期:首次接入,先不阻塞合并。
# 转正条件:连续两次 CI 全绿后删掉这一行,改为阻塞。
continue-on-error: true
env:
# Windows 控制台默认 cp936,中文用例名/日志会乱码甚至抛 UnicodeEncodeError。
# 采集器 stdout 是 JSON 状态协议,一旦被编码污染,Swift 端会误判"未运行"。
PYTHONUTF8: "1"
PYTHONIOENCODING: "utf-8"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version-file: '.python-version'
- name: 显示版本
run: python --version
- name: 版本一致性检查
run: python Scripts/bin/check_python_version.py --strict
- name: 语法编译检查
run: >-
python -m compileall -q
Scripts/bin
Services/findatahub-core/src
Services/findatahub-client/src
- name: 安装依赖
# 显式列依赖而不装各服务的 requirements:
# 这里要验的是跨平台代码路径,不是完整运行环境。
run: |
python -m pip install --quiet pytest pytest-asyncio
python -m pip install --quiet ./Services/findatahub-core
python -m pip install --quiet ./Services/findatahub-client
python -m pip install --quiet pymysql httpx pandas sqlalchemy
- name: 跨平台抽象层测试(本次 Windows 接入的核心)
# 32 条用例锁定:paths 的 Windows 分支、process_utils 的
# OpenProcess / taskkill / creationflags、UTF-8 环境构造
run: python -m pytest Scripts/bin/tests/test_paths_process_utils.py -v
- name: 脚本层测试
# 含 PidManager、DB 池等;这批用例此前只在 POSIX 上跑过,
# Windows 上首次运行,暴露的问题正是需要修的
run: python -m pytest Scripts/bin/tests -q
- name: 数据层测试
# 排除 integration:Windows job 未接 MySQL service,跑它只会得到一片 skip
run: python -m pytest Services/findatahub-core/tests -q --ignore=Services/findatahub-core/tests/integration
- name: SDK 测试
run: python -m pytest Services/findatahub-client/tests -q