Files
FinDataHub 21606708d0 ci(windows): 实测 akshare 可行性,推翻「硬阻塞点」假设
## 假设被推翻

迁移计划曾把 akshare 的 py_mini_racer(62MB V8 预编译)列为「唯一硬阻塞点」,
理由是 Windows + cp312 是否有 wheel 未验证。

2026-10-26 用 uv 做离线解析(无需 Windows 机器):

    uv pip install --python-platform windows --python-version 3.12 --dry-run akshare
    → 解析成功,mini-racer==0.14.1,与本机 macOS 上的版本完全一致
    → pandas/numpy 仅小版本差异(3.1.0/2.5.4 vs 本机 3.0.3/2.4.6)

## 但「能装」不等于「能跑」

V8 原生库在 win32 上能否真正加载并执行 JS 必须实测。故选在观察期的
windows job 里加三步实测(各自 continue-on-error,避免装不上时中断后续步骤):

1. 安装 akshare
2. 验证 py_mini_racer 可执行 JS(ctx.eval('1+1') == 2)
3. 跑 Services/akshare_fetcher/tests

## 一个必须写死的命名陷阱

PyPI 包名是 mini-racer,但**顶层模块名仍是 py_mini_racer**
(akshare 源码里全是 import py_mini_racer)。写成 import mini_racer 会
ModuleNotFoundError,从而误判为「Windows 装不上」。

本机已验证正确写法:import py_mini_racer → ctx.eval('1+1') == 2,
akshare 1.18.64 及其依赖 mini-racer 的模块均可正常导入。

首次 CI 反馈后:若三步全绿,可去掉 continue-on-error,并考虑把 tushare
一并纳入 Windows 范围。
2026-10-26 21:07:59 +08:00

238 lines
11 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.
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
# ── akshare 可行性实测 ──────────────────────────────────
#
# 迁移计划曾把 akshare 的 py_mini_racer(62MB V8 预编译)列为「唯一硬阻塞点」,
# 理由是"Windows 上是否有 cp312 wheel 未验证"。
#
# 2026-10-26 用 uv 做离线解析,结论是**可以**:
# uv pip install --python-platform windows --python-version 3.12 --dry-run akshare
# → 解析成功,其中 mini-racer==0.14.1,与本机 macOS 上的版本完全一致
# (pandas/numpy 仅小版本差异:CI 3.1.0/2.5.4 vs 本机 3.0.3/2.4.6)
#
# 但"能装"不等于"能跑"——V8 二进制在 win32 上能否真正加载需要实测。
# 下面两步就是这次实测。各自 continue-on-error,避免装不上时中断后续步骤;
# 整个 job 本身也在观察期,失败不阻塞合并。
- name: 安装 akshare(Windows 可行性实测)
continue-on-error: true
run: python -m pip install --quiet akshare
- name: 验证 mini-racer 原生库可加载
# 注意包名与模块名不一致:PyPI 包叫 mini-racer,**顶层模块仍是 py_mini_racer**
# (akshare 源码里也全是 import py_mini_racer / from py_mini_racer import MiniRacer)。
# 写成 import mini_racer 会 ModuleNotFoundError 而误判为"Windows 装不上"。
continue-on-error: true
run: >-
python -c "import py_mini_racer;
ctx = py_mini_racer.MiniRacer();
print('py_mini_racer 可执行 JS:', ctx.eval('1+1'))"
- name: akshare 采集器测试
continue-on-error: true
run: python -m pytest Services/akshare_fetcher/tests -q