ci: replace the long-dead Travis config with a GitHub Actions gate (#349)
* fix(test): replace collections.Callable, removed in Python 3.10
test_client_try_import used `isinstance(x, collections.Callable)`.
`collections.Callable` has been an alias for `collections.abc.Callable`
since 3.3 and was removed in 3.10, so the test raises:
AttributeError: module 'collections' has no attribute 'Callable'
The suite therefore only passes on Python <= 3.9. .travis.yml tests
3.5/3.6/3.7, which is why this was never noticed -- and Travis has not
run on this repo for a long time either.
Uses the builtin `callable()` instead of `collections.abc.Callable`:
same semantics (collections.abc.Callable.__subclasshook__ just checks
for __call__), and it drops the now-unused `collections` import rather
than keeping one around for a single predicate.
Verified on Python 3.11.9:
before 1 failed, 47 passed, 37 skipped
after 48 passed, 37 skipped
* ci: 从零建 GitHub Actions 门禁,替换早已停用的 Travis
本仓此前 .github/ 下只有 PR 模板,唯一的 CI 是 .travis.yml(travis-ci.org 已下线,
且只测 3.5/3.6/3.7)——等于 ucloud/services 下全部生成代码从来没有过闸门。
codegen PR 要能自动合并,前提是先有一个「会红」的门禁;但这个门禁必须只对本轮
产物负责,否则它自己就成了自动合并的阻塞源。
- 新增 .github/workflows/ci.yml:触发限 pull_request/push 到 master(不用裸
[push, pull_request]),显式 permissions: contents: read;ci-syntax / lint /
test-cov 三个 job 汇总成恒名的 ci-gate(needs + if: always(),任一
failure/cancelled 即 exit 1),分支保护只需把 ci-gate 配成 required check。
codecov 上传是 test job 内的 step 且 continue-on-error,不进 ci-gate 的 needs
——外部服务抖动不该拦下合并。所有语言命令都经 Makefile target,yml 里不写裸命令。
- 新增 scripts/ci-syntax.sh,Makefile 的 ci-syntax 改为调它。两条闸都只用 stdlib:
G1 `compileall -q ucloud` 覆盖全部生成代码(现有 lint 显式
--exclude=ucloud/services,恰好把生成代码排除在外,这层一直是空的)。
G2 `scripts/dup-check.py` 用 ast 补编译器抓不住的重复声明——python 里重复的
def/class 不是语法错误,后者静默覆盖前者,compileall 一律 exit 0。
- 判定边界:**全量扫描整棵 ucloud/,但只对本轮变更的文件阻断**,其余命中打印成
告警、不计入退出码。原因是 G2 在本仓存量就有一处命中——uphost 的
get_phost_disk_upgrade_price 被生成了两遍,后者是已删除 API 的旧快照(publish
集合追加式存储,保留了 API 改名前的形态),根因在数据侧、改 SDK 仓库无效。
整棵树一律阻断的话,发**任何一个**产品(UDisk、UNet……)的 codegen PR 都会被
这处与本次发布毫无关系的问题判红,mergeable_state 变 unstable,自动合并永久
失效——那正是这套门禁要解决的问题本身。
同源决策见 ucloud-sdk-release/scripts/python-fatal-check.sh(2026-07-31),
已被 pipeline 2507836 实证:那次发的是 UHost+UAI-Modelverse,根本没重新生成
uphost,那处重复照样被扫了出来。
变更集取环境变量 CI_BASE_SHA:`git diff --name-only $CI_BASE_SHA...HEAD` 里
ucloud/ 下的 .py(三点式取 merge-base→HEAD,正好等于本 PR 引入的改动;
--diff-filter=d 排除删除项)。yml 里 PR 事件给 base.sha、push 事件给 before,
checkout 配 fetch-depth: 0(浅克隆下 base sha 不在本地对象库,必然解析不出)。
未设置或解析不出(force push、全零 sha、无共同祖先)→ fail-closed 退回整棵树
严格判定并打印退回原因。本地不设该变量看到的就是整棵树严格判定,与退回路径
一致,不存在「本地绿 / CI 红」的分叉。
dup-check.py 因此加了 --strict-list(仍全量扫描,只有清单内文件的命中才阻断),
原有的 `dup-check.py <目录>...` 用法不变,本地与退回路径都走它。
- ci-syntax 里**不放** `black --check`,与 go 的 ci-syntax 带 gofmtcheck.sh 不对称,
是有意为之:生成侧 ucloud-sdk-release 的 python:format job 用
`pip install -U black` + `make fmt`,永远拿当天最新版格式化后提交;而 black
每年改一次稳定风格(实测本仓 392 个文件在 24.8.0/24.10.0 下全过,25.1.0 起有
211 个被判需重排)。校验端无论钉哪个版本,迟早与生成侧偏斜、把每个 codegen PR
恒判为红——同样是自动合并要解决的问题本身。格式化归生成侧,门禁只管代码能不能
用:非法标识符这类真缺陷 compileall 已经拦得住(实测 `def uai-modelverse(self):`
→ exit 1,而纯格式的 `def foo( a,b ):` → exit 0)。理由写在 Makefile 注释里。
- Makefile test-cov 补 --cov-report=term --cov-report=xml:codecov-action 只上传
现成的报告文件,不像 Travis 时代的 codecov bash uploader 会顺手跑 coverage xml;
不出 xml 的话 README 上那个 Codecov 徽章就再也不会更新。
- 新增 .github/workflows/compat.yml:只挂 schedule + workflow_dispatch,不挂
pull_request/push——它问的是「SDK 今天还能跑在哪些 python 上」,与单个 PR 改了
什么无关,挂到 PR 上只会在每个 codegen PR 上多出一排无关 check、干扰门禁判读。
矩阵 3.8–3.14、fail-fast: false。下界 3.8 是实测结论,不是 setup.py 声明的 3.5:
actions/python-versions 对 3.7 最后只出到 3.7.17 且只有 linux-20.04/22.04 产物,
而 ubuntu-latest 现指向 Ubuntu 24.04,setup-python 根本装不出 3.7;3.8.18 有
linux-24.04 产物,且本地 3.8.20 上 pip install -e .[ci]、make lint、make test-cov
均 exit 0(48 passed / 37 skipped)。
- 删 .travis.yml:服务已下线,留着只会让人误以为还有 CI 在跑。
setup.py 不动:CI 不跑 black,[ci] 就不需要它;在 [ci] 里钉 black 版本反而会把
生成侧 python:format 那步的 `pip install -e ".[ci]"` 降级,干扰生成端。
本地实跑(macOS bash 3.2 + python 3.11.9):
不设 CI_BASE_SHA → exit 1,G2 报出 uphost 那处存量
CI_BASE_SHA=父提交、只改干净的 udisk → exit 0,uphost 那处降为告警仍打印
同上但 udisk 注入语法错 / 重复方法 → exit 1(对本轮变更文件仍然阻断)
CI_BASE_SHA 给不存在的 sha / 全零 sha → 退回整棵树严格判定,exit 1,打印退回原因
make lint / make test-cov → exit 0,未受影响
* ci: 去掉 compat.yml
兼容矩阵是纯巡检,不参与门禁判定,也没人会盯一个非阻断的定时任务。
声明下界还成不成立是另一个问题,要验的时候手工跑一次即可,
不必为它常驻一条每天运行的 workflow。 E
Episkey committed
67c2b2e297d57c7dc46579f7e9e9eed5832f5b6c
Parent: dbfa697
Committed by GitHub <noreply@github.com>
on 8/19/2026, 8:49:08 AM