create-dwy 0.14.6 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/bin/index.js +4 -0
- package/package.json +1 -1
- package/src/skills-install.js +113 -0
- package/src/sync-all.js +47 -3
- package/templates/ai-tools/CLAUDE.md +18 -1
- package/templates/ai-tools/hooks/Git/pre-git-commit-sensitive-check.sh +10 -2
- package/templates/ai-tools/rules/Android/dwy-android-core.md +8 -0
- package/templates/ai-tools/rules/Flutter/dwy-flutter-core.md +8 -0
- package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-strict.md +221 -0
- package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +193 -0
- package/templates/ai-tools/rules/iOS/dwy-ios-pitfalls.md +159 -0
- package/templates/ai-tools/rules/iOS/dwy-swift-core.md +35 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-acceptance/SKILL.md +23 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +28 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-competitor/SKILL.md +22 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-ideate/SKILL.md +21 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-mvp/SKILL.md +23 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-poc/SKILL.md +24 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prd/SKILL.md +22 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/SKILL.md +68 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +103 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +23 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-release/SKILL.md +26 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-design/SKILL.md +23 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-dev/SKILL.md +22 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-launch/SKILL.md +25 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-prd/SKILL.md +22 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-ship/SKILL.md +21 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tasks/SKILL.md +22 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tdd-dev/SKILL.md +25 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-validate/SKILL.md +28 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-version/SKILL.md +23 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/SKILL.md +365 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/references/electron-release-workflow-template.yml +98 -0
- package/templates/ai-tools/skills//{350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel → 345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish}/references/release-workflow-template.yml +3 -3
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +31 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +31 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +23 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +37 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +23 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +27 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +25 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +32 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/monitor-notify.md +36 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/publish-config.md +65 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/version-sync.md +30 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/scripts/release_config.sh +115 -0
- package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/SKILL.md +31 -21
- package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/python-commercial-guide.md +1 -1
- package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/sensitive-patterns.md +1 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/init_config.sh +91 -0
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-whiteboard-prototype/SKILL.md +70 -0
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-whiteboard-prototype/assets/template.html +353 -0
- package/templates/ai-tools/rules/Global/dwy-common-code-baseline.md +0 -27
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-sdk-spec/scripts/init_config.sh +0 -97
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/SKILL.md +0 -50
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/references/application-release.md +0 -106
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/references/sdk-release.md +0 -93
- package/templates/ai-tools/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel/SKILL.md +0 -356
- /package/templates/ai-tools/rules/{Global → Docker}/dwy-docker.md +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/files-whitelist-snippet.json" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/js-commercial.npmignore" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/python-commercial-MANIFEST.in" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/python-commercial.gitignore" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/js-commercial-guide.md" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/publish-blacklist.md" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/check_comments.py" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/check_js_publish.sh" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/check_python_publish.sh" +0 -0
- /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/detect_sdk.sh" +0 -0
- /package/templates/ai-tools/skills//345/{274/200/345/217/221/346/265/201/347/250/213 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-semver/SKILL.md" +0 -0
- /package/templates/ai-tools/skills//345/{274/200/345/217/221/346/265/201/347/250/213 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-semver/references/semver-spec.md" +0 -0
- /package/templates/ai-tools/skills//345/{274/200/345/217/221/346/265/201/347/250/213 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-semver/scripts/bump.py" +0 -0
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# SDK 发版流程
|
|
2
|
-
|
|
3
|
-
适用:发布目标是 npm、PyPI、私有包仓库或对外库版本。
|
|
4
|
-
|
|
5
|
-
## 主流程
|
|
6
|
-
|
|
7
|
-
流程骨架:
|
|
8
|
-
|
|
9
|
-
1. `bump`
|
|
10
|
-
2. `CHANGELOG`
|
|
11
|
-
3. `build`
|
|
12
|
-
4. `publish package`
|
|
13
|
-
5. `verify registry`
|
|
14
|
-
|
|
15
|
-
## 1. 判断是否需要 bump
|
|
16
|
-
|
|
17
|
-
SDK 发版默认需要判断版本升级。
|
|
18
|
-
|
|
19
|
-
- **需要 bump**:正式发布新版本、预发布版本、修复发布产物并重发
|
|
20
|
-
- **拿不准**:调用 `dwy-semver` 决定升级位和新版本号
|
|
21
|
-
|
|
22
|
-
执行顺序:
|
|
23
|
-
|
|
24
|
-
1. 读取当前版本
|
|
25
|
-
2. 算出新版本号
|
|
26
|
-
3. 修改版本文件
|
|
27
|
-
|
|
28
|
-
不要用会自动 commit 的版本命令。直接编辑版本文件。
|
|
29
|
-
|
|
30
|
-
## 2. 写 CHANGELOG
|
|
31
|
-
|
|
32
|
-
SDK 发版必须写 `CHANGELOG`。
|
|
33
|
-
|
|
34
|
-
处理顺序:
|
|
35
|
-
|
|
36
|
-
1. 找上一个 release tag
|
|
37
|
-
2. 查看 `git log <last-tag>..HEAD --oneline`
|
|
38
|
-
3. 按 `feat` / `fix` / `refactor` / `chore` 分组
|
|
39
|
-
4. 写入 `CHANGELOG.md` 或项目指定的 release notes 文件
|
|
40
|
-
|
|
41
|
-
如果仓库已有自动生成命令,优先使用;没有再手写。
|
|
42
|
-
|
|
43
|
-
`CHANGELOG` 未完成,不继续。
|
|
44
|
-
|
|
45
|
-
## 3. 确认测试已完成并执行 build
|
|
46
|
-
|
|
47
|
-
本流程不重复跑测试,只确认当前变更已经过项目要求的测试。
|
|
48
|
-
|
|
49
|
-
然后执行 build,确保包产物可生成。
|
|
50
|
-
|
|
51
|
-
构建失败则停止。
|
|
52
|
-
|
|
53
|
-
## 4. 决定 publish 入口
|
|
54
|
-
|
|
55
|
-
根据仓库上下文判断:
|
|
56
|
-
|
|
57
|
-
- **CI 发布**:workflow、release job、tag push、workflow_dispatch、release please、cibuildwheel
|
|
58
|
-
- **本地发布**:`pnpm publish`、`uv publish`、`twine upload` 或其他本地发包命令
|
|
59
|
-
|
|
60
|
-
## 5. 执行 publish package
|
|
61
|
-
|
|
62
|
-
如果是 CI 驱动:
|
|
63
|
-
|
|
64
|
-
1. 提交 release 相关文件
|
|
65
|
-
2. 按项目规则创建 tag;如果不是 tag 触发,则按配置触发 workflow
|
|
66
|
-
3. 推送分支和 tag
|
|
67
|
-
4. 观察发布运行状态直到成功或失败
|
|
68
|
-
|
|
69
|
-
如果是本地驱动:
|
|
70
|
-
|
|
71
|
-
1. 提交 release 相关文件
|
|
72
|
-
2. 按项目规则创建 tag
|
|
73
|
-
3. 推送分支和 tag
|
|
74
|
-
4. 执行本地发包命令
|
|
75
|
-
|
|
76
|
-
失败则停止,并给出失败阶段和关键报错。
|
|
77
|
-
|
|
78
|
-
## 6. verify registry
|
|
79
|
-
|
|
80
|
-
发布完成后,确认目标仓库中的新版本已可见。验证方式包括:
|
|
81
|
-
|
|
82
|
-
- npm:`npm view <pkg> version`
|
|
83
|
-
- PyPI:`pip index versions <pkg>` 或项目定义的检查命令
|
|
84
|
-
- 私有仓库:项目约定的版本查询方式
|
|
85
|
-
- GitHub Release:确认 release/tag 已生成
|
|
86
|
-
|
|
87
|
-
验证失败,发版不算完成。
|
|
88
|
-
|
|
89
|
-
## 禁止事项
|
|
90
|
-
|
|
91
|
-
- 禁止跳过版本升级判断
|
|
92
|
-
- 禁止先发包后补 `CHANGELOG`
|
|
93
|
-
- 禁止把包仓库验证替换成环境健康检查
|
|
@@ -1,356 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: dwy-cibuildwheel
|
|
3
|
-
description: "用 GitHub Actions + cibuildwheel 把【含 C 扩展 / Cython / .so 编译产物】的 Python 包跨平台 build wheel(linux/macos/windows × x86_64/arm64)并通过 PyPI Trusted Publisher (OIDC) 发布到 PyPI 的避坑指南。仅适用于需要编译 .so 的包;纯 Python 包发 PyPI 不需要本 skill(走 dwy-publish + dwy-sdk-spec)。涵盖 PEP 639 license / cibuildwheel 版本 tag / YAML 冒号转义 / {package} 占位符 / 跨平台 shell 引号 / manylinux 镜像 / 测试阶段 binary 强制 / GitHub runner image 退役(macos-13 已死)等 8 类高频坑。触发条件:编辑或新建含 cibuildwheel 的 .github/workflows/*.yml、pyproject.toml 含 Cython / ext_modules、用户说 'cibuildwheel' / 'build wheel' / '跨平台 wheel' / '.so 跨平台打包' / 'wheel build 失败' / 'macos runner 一直 queued',或要把含 C 扩展的 Python 包发 PyPI 时。"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# dwy-cibuildwheel — Python Cython SDK 跨平台 PyPI 发布避坑
|
|
7
|
-
|
|
8
|
-
适用场景:含 Cython 编译扩展的 Python 包、跨平台 wheel 分发(linux x86_64/aarch64 + macos x86_64/arm64 + windows AMD64)、GitHub Actions + cibuildwheel + PyPI Trusted Publisher OIDC 发版。
|
|
9
|
-
|
|
10
|
-
> 定位:本 skill 只解决「含 C 扩展的包怎么用 GitHub Actions 跨平台打 wheel 发 PyPI」的 CI 避坑。通用发版流程(测试 / bump / changelog / tag)走 `dwy-publish`;发布前接口脱敏 / 源码泄露 / 产物审计走 `dwy-sdk-spec`;纯 Python 包不需要本 skill。
|
|
11
|
-
|
|
12
|
-
**实战来源**:quant-sdk (quantzone) 0.4.0 首发到 PyPI 过程中暴露的 8 类坑,全部沉淀于此。
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## 8 个高频坑
|
|
17
|
-
|
|
18
|
-
### 坑 1:PEP 639 license expression 触发 build env 依赖冲突
|
|
19
|
-
|
|
20
|
-
**症状**(任意平台 build 阶段):
|
|
21
|
-
```
|
|
22
|
-
ImportError: Cannot import `packaging.licenses`.
|
|
23
|
-
Setuptools>=77.0.0 requires "packaging>=24.2" to work properly.
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
**根因**:`pyproject.toml` 用了 PEP 639 字符串形式 `license = "MIT"`。setuptools 77+ 校验 license expression 时强依赖 `packaging>=24.2`,但跨平台 cibuildwheel 临时 build env 装到老 packaging 时直接撞死。
|
|
27
|
-
|
|
28
|
-
**修复**:在 `[build-system].requires` 显式锁两个下限:
|
|
29
|
-
```toml
|
|
30
|
-
[build-system]
|
|
31
|
-
requires = [
|
|
32
|
-
"setuptools>=77",
|
|
33
|
-
"packaging>=24.2",
|
|
34
|
-
"wheel",
|
|
35
|
-
"Cython>=3.0.10",
|
|
36
|
-
]
|
|
37
|
-
build-backend = "setuptools.build_meta"
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
**例外**:还在用老语法 `license = {text = "MIT"}` / `license = {file = "LICENSE"}` 的项目不触发。但 PEP 639 是趋势,建议主动对齐 + 提前锁依赖。
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
### 坑 2:cibuildwheel action 必须用 minor 级或精确 tag
|
|
45
|
-
|
|
46
|
-
**症状**(workflow 启动几秒就 fail,错误页面提示):
|
|
47
|
-
```
|
|
48
|
-
Unable to resolve action `pypa/cibuildwheel@v3`, unable to find version `v3`
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
**根因**:不像 `actions/checkout@v4` 那种 major moving alias,`pypa/cibuildwheel` **只维护 minor 级 moving tag**(`v3.4`)和精确 patch tag(`v3.4.1`),**没有 `v3` 这种 major alias**。
|
|
52
|
-
|
|
53
|
-
**修复**:
|
|
54
|
-
```yaml
|
|
55
|
-
- uses: pypa/cibuildwheel@v3.4 # minor moving,自动跟 v3.4.x patch(推荐)
|
|
56
|
-
# 或精确锁
|
|
57
|
-
- uses: pypa/cibuildwheel@v3.4.1
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
**自检**:改 action 版本前先查实际存在的 tag:
|
|
61
|
-
```bash
|
|
62
|
-
curl -sL https://api.github.com/repos/pypa/cibuildwheel/git/refs/tags \
|
|
63
|
-
| python3 -c "import sys,json; tags=[r['ref'].replace('refs/tags/','') for r in json.load(sys.stdin)]; print([t for t in tags if t.startswith('v3')][-10:])"
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
通用教训:**不要假设任何 GitHub Action 都有 `v<N>` major alias**,必须先查仓库 tag 列表。
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
### 坑 3:YAML 字符串含冒号必须加引号(静默杀手)
|
|
71
|
-
|
|
72
|
-
**症状**:workflow 触发后几秒 conclusion=failure,但 `gh run view <id> --json jobs` 显示 **jobs 数组为空**——一个 job 都没启动。
|
|
73
|
-
|
|
74
|
-
**根因**:GitHub Actions 在 yaml 解析失败时直接拒绝 workflow,不启动任何 job。最常见触发:
|
|
75
|
-
```yaml
|
|
76
|
-
CIBW_BEFORE_TEST: pip install --only-binary=:all: pyarrow # 错!
|
|
77
|
-
```
|
|
78
|
-
`:all:` 第二个冒号被 YAML 解析为 mapping value delimiter,整个文件解析失败。
|
|
79
|
-
|
|
80
|
-
**修复**:整行加引号:
|
|
81
|
-
```yaml
|
|
82
|
-
CIBW_BEFORE_TEST: "pip install --only-binary=:all: pyarrow pandas"
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
**强制自检**(改完任何 workflow 后必跑):
|
|
86
|
-
```bash
|
|
87
|
-
python3 -c "import yaml; yaml.safe_load(open('.github/workflows/release.yml'))"
|
|
88
|
-
```
|
|
89
|
-
本地报错就修,不要 push 后等 GitHub 告诉你(GitHub 不给具体 yaml 错误行号,得自己查)。
|
|
90
|
-
|
|
91
|
-
**调试线索**:run conclusion=failure + jobs 为空 → 99% yaml 解析问题。
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
### 坑 4:CIBW_TEST_COMMAND 用 `{package}` 不要用 `{project}`
|
|
96
|
-
|
|
97
|
-
**症状**(monorepo + SDK 在子目录):
|
|
98
|
-
```
|
|
99
|
-
ERROR: file or directory not found: /runner/work/<repo>/<repo>/tests
|
|
100
|
-
collected 0 items
|
|
101
|
-
============================ no tests ran in 0.00s =============================
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
**根因**:`{project}` 占位符替换为 cibuildwheel 调用的工作目录(**repo 根**),不是 `package-dir`。如果 SDK 在 `quant-sdk/` 子目录,测试在 `quant-sdk/tests/`,但 `{project}/tests` 会找 `<repo-root>/tests`,找不到。
|
|
105
|
-
|
|
106
|
-
**修复**:用 `{package}` 占位符:
|
|
107
|
-
```yaml
|
|
108
|
-
- uses: pypa/cibuildwheel@v3.4
|
|
109
|
-
with:
|
|
110
|
-
package-dir: quant-sdk
|
|
111
|
-
env:
|
|
112
|
-
CIBW_TEST_COMMAND: 'pytest {package}/tests -v -k "not requires_server"'
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
**占位符对照表**:
|
|
116
|
-
|
|
117
|
-
| 占位符 | 替换为 |
|
|
118
|
-
|---|---|
|
|
119
|
-
| `{project}` | cibuildwheel 调用的工作目录(repo 根) |
|
|
120
|
-
| `{package}` | `package-dir` 的绝对路径 |
|
|
121
|
-
|
|
122
|
-
**适用场景**:monorepo 必须用 `{package}`;单包仓库 `{project}` `{package}` 等价。习惯用 `{package}` 不会错。
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
### 坑 5:跨平台 shell 引号必须外单内双
|
|
127
|
-
|
|
128
|
-
**症状**(仅 Windows,linux/macos 正常):
|
|
129
|
-
```
|
|
130
|
-
+ pytest D:\a\repo\repo\quant-sdk/tests -v -k 'not requires_server'
|
|
131
|
-
ERROR: file or directory not found: requires_server'
|
|
132
|
-
collecting ... collected 0 items
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
**根因**:Windows cmd **不解析单引号字符串**。`-k 'not requires_server'` 在 cmd 里被拆成两个独立参数 `'not` 和 `requires_server'`,pytest 把第二个当成目录路径。
|
|
136
|
-
|
|
137
|
-
**修复**:YAML value 外层用**单引号**,里面 shell 命令用**双引号**(双引号在 sh + cmd 都正常):
|
|
138
|
-
```yaml
|
|
139
|
-
CIBW_TEST_COMMAND: 'pytest {package}/tests -v -k "not requires_server"'
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
**反例**(外双内单 = Windows 必坏):
|
|
143
|
-
```yaml
|
|
144
|
-
CIBW_TEST_COMMAND: "pytest {package}/tests -v -k 'not requires_server'" # 错!
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
**通用原则**:跨平台跑的命令字符串里,shell 引号统一用双引号;YAML 引号统一用单引号。**永远不要在跨平台命令里用单引号包参数**。
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
### 坑 6:manylinux 镜像必须升到 `_2_28`
|
|
152
|
-
|
|
153
|
-
**症状**(仅 linux 容器内 pip install 阶段):
|
|
154
|
-
```
|
|
155
|
-
ERROR: Could not find a version that satisfies the requirement pyarrow (from versions: none)
|
|
156
|
-
ERROR: No matching distribution found for pyarrow
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
**根因**:`pyarrow` / `pandas` / `numpy` 等大型 C 扩展库**早已只发 `manylinux_2_28` wheel**(要求 glibc 2.28+,CentOS 8+ / Ubuntu 18.04+ / Debian 10+),**不再支持 `manylinux2014`**(glibc 2.17)。cibuildwheel 老配置默认起 `manylinux2014` 容器,容器内 pip 找不到任何匹配 wheel。
|
|
160
|
-
|
|
161
|
-
**修复**:
|
|
162
|
-
```yaml
|
|
163
|
-
env:
|
|
164
|
-
CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
|
|
165
|
-
CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28
|
|
166
|
-
# musllinux 通常不用动
|
|
167
|
-
CIBW_MUSLLINUX_X86_64_IMAGE: musllinux_1_2
|
|
168
|
-
CIBW_MUSLLINUX_AARCH64_IMAGE: musllinux_1_2
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
**风险评估**:升 manylinux_2_28 提高了用户机器的 glibc 要求,但你的 SDK 既然依赖 pyarrow,用户本来就受这个约束,**所以不增加额外限制**。
|
|
172
|
-
|
|
173
|
-
**判断标准**:你的运行时依赖里有 pyarrow / pandas / numpy / scipy / lxml 等 → 必须 `manylinux_2_28`。
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
### 坑 7:测试阶段三方依赖必须强制 binary
|
|
178
|
-
|
|
179
|
-
**症状**(任意平台、最容易在 aarch64 触发):
|
|
180
|
-
```
|
|
181
|
-
ERROR: Failed building wheel for libcst
|
|
182
|
-
error: can't find Rust compiler
|
|
183
|
-
ERROR: Failed to build 'pyarrow' when installing build dependencies for pyarrow
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
**根因**:cibuildwheel 在 test 阶段 `pip install <built-wheel>` 时自动装 SDK 的 runtime deps。pip 默认 prefer binary 但**允许源码 fallback**——某些版本/架构组合下 pip 找不到合适 binary,回退源码 build pyarrow → pyarrow build dep 拉 libcst → libcst 需要 Rust → 容器内没装 → 整个链 fail。
|
|
187
|
-
|
|
188
|
-
**修复**:在 test 前预装大依赖,**强制只用 binary**:
|
|
189
|
-
```yaml
|
|
190
|
-
env:
|
|
191
|
-
CIBW_TEST_REQUIRES: pytest pytest-asyncio respx
|
|
192
|
-
CIBW_BEFORE_TEST: "pip install --only-binary=:all: pyarrow pandas"
|
|
193
|
-
CIBW_TEST_COMMAND: 'pytest {package}/tests -v -k "not requires_server"'
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
`--only-binary=:all:` 表示对**所有包**拒绝任何源码 build。找不到合适 wheel 直接 fail(快速明确),不会拉长得离谱的 build chain。
|
|
197
|
-
|
|
198
|
-
**关键**:必须搭配坑 6 的 `manylinux_2_28`,否则 pip 找不到 binary 直接报"No matching distribution"(参见坑 6 现象)。
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
### 坑 8:macos-13 runner 已退休(2025-12 起死等)
|
|
203
|
-
|
|
204
|
-
**症状**:workflow 长期卡在
|
|
205
|
-
```
|
|
206
|
-
Requested labels: macos-13
|
|
207
|
-
Waiting for a runner to pick up this job...
|
|
208
|
-
```
|
|
209
|
-
小时级 queued,runner 永远不分配。其他 job 都成功,publish 因 `needs: build_wheels` 一直不启动。
|
|
210
|
-
|
|
211
|
-
**根因**:GitHub 已于 **2025-12-04** 正式退役 `macos-13` runner image。配 `runs-on: macos-13` 的 job 不是排队,是 **runner pool 已被删空**——永远等不到。GitHub 没有为这种 case 抛错,只让你 queued 着。
|
|
212
|
-
|
|
213
|
-
Apple 完全停 Intel + GitHub 计划 **2027 秋停所有 x86_64 macOS runner**,整个 Intel Mac runner 时代在落幕。
|
|
214
|
-
|
|
215
|
-
**修复**:根据是否需要 macOS x86_64 wheel 选一:
|
|
216
|
-
|
|
217
|
-
```yaml
|
|
218
|
-
matrix:
|
|
219
|
-
include:
|
|
220
|
-
- { os: ubuntu-latest, archs: x86_64 }
|
|
221
|
-
- { os: ubuntu-24.04-arm, archs: aarch64 }
|
|
222
|
-
# 选项 A: 仍需 x86_64 wheel(开发者自己是 Intel Mac / 重要用户群有 Intel Mac)
|
|
223
|
-
- { os: macos-15-intel, archs: x86_64 } # ✅ 替代死掉的 macos-13(真 Intel runner)
|
|
224
|
-
# 选项 B: 不需要 x86_64,删掉上一行,Intel Mac 用户走 Rosetta 跑 arm64 wheel
|
|
225
|
-
- { os: macos-14, archs: arm64 }
|
|
226
|
-
- { os: windows-latest, archs: AMD64 }
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
**其他可选替代 label**:
|
|
230
|
-
|
|
231
|
-
| label | 说明 |
|
|
232
|
-
|---|---|
|
|
233
|
-
| `macos-15-intel` | 真 Intel Mac runner(新引入,推荐) |
|
|
234
|
-
| `macos-14-large` / `macos-15-large` | Apple Silicon 上虚拟化跑 x86_64,贵且慢 |
|
|
235
|
-
|
|
236
|
-
**通用教训**:GitHub runner image 会按计划退役(macos-11/12/13 已退,macos-14 在用,macos-15 是新主力)。改 workflow 前去 https://github.com/actions/runner-images 看当前 supported labels;不要假设 stackoverflow 上的老 yaml 还能跑。
|
|
237
|
-
|
|
238
|
-
**自检**: 看 `Waiting for a runner` 卡超过 30 分钟,基本 100% 是 label 已死(而不是真排队)。
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## 完整 workflow 模板
|
|
243
|
-
|
|
244
|
-
直接复制 `references/release-workflow-template.yml` 到项目 `.github/workflows/release.yml`,只改两处:
|
|
245
|
-
|
|
246
|
-
1. `package-dir: <PACKAGE_DIR>` → 实际 SDK 子目录(monorepo 必填)
|
|
247
|
-
2. `CIBW_BUILD` 行的 Python 版本枚举(按需调整)
|
|
248
|
-
|
|
249
|
-
模板已内置全部 7 项修复,开箱即用。
|
|
250
|
-
|
|
251
|
-
---
|
|
252
|
-
|
|
253
|
-
## Pre-flight checklist(发版前必跑)
|
|
254
|
-
|
|
255
|
-
```bash
|
|
256
|
-
# 1. 工作树干净
|
|
257
|
-
git status
|
|
258
|
-
|
|
259
|
-
# 2. tag 必须 = pyproject 版本(避开 workflow version_check 失败)
|
|
260
|
-
grep '^version = ' <PACKAGE_DIR>/pyproject.toml
|
|
261
|
-
git tag -l 'v*'
|
|
262
|
-
|
|
263
|
-
# 3. PyPI 上无该版本(PyPI 版本号永久不可重用!)
|
|
264
|
-
curl -s https://pypi.org/pypi/<your-package>/json \
|
|
265
|
-
| python3 -c "import sys,json; print(list(json.load(sys.stdin).get('releases',{}).keys()))"
|
|
266
|
-
|
|
267
|
-
# 4. CHANGELOG 有新版本条目
|
|
268
|
-
head -20 <PACKAGE_DIR>/CHANGELOG.md
|
|
269
|
-
|
|
270
|
-
# 5. Trusted Publisher 已配(PyPI 网页 https://pypi.org/manage/account/publishing/ 确认)
|
|
271
|
-
# owner = GitHub org,repo = GitHub repo 名(仓库改过名要同步改 PyPI!)
|
|
272
|
-
# workflow = release.yml,environment = pypi
|
|
273
|
-
|
|
274
|
-
# 6. GitHub Environment "pypi" 存在
|
|
275
|
-
gh api repos/<owner>/<repo>/environments --jq '.environments[].name'
|
|
276
|
-
|
|
277
|
-
# 7. workflow yml 本地解析通过
|
|
278
|
-
python3 -c "import yaml; yaml.safe_load(open('.github/workflows/release.yml'))"
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
---
|
|
282
|
-
|
|
283
|
-
## Build 失败后的安全重发流程
|
|
284
|
-
|
|
285
|
-
**前提**:PyPI 上还**没有 publish 成功**该版本号(PyPI 一旦 publish 成功,号码永久占用,必须 bump 下一个版本)。
|
|
286
|
-
|
|
287
|
-
```bash
|
|
288
|
-
# 1. cancel 失败 run,节省 CI 额度
|
|
289
|
-
gh run cancel <run-id>
|
|
290
|
-
|
|
291
|
-
# 2. 修代码 + commit + push
|
|
292
|
-
git add <修改文件>
|
|
293
|
-
git commit -m "ci(sdk): 修复 xxx"
|
|
294
|
-
git push origin main
|
|
295
|
-
|
|
296
|
-
# 3. 删本地 + 远端 tag,重打指向新 commit
|
|
297
|
-
git push origin :refs/tags/v0.4.0
|
|
298
|
-
git tag -d v0.4.0
|
|
299
|
-
git tag v0.4.0
|
|
300
|
-
git push origin v0.4.0
|
|
301
|
-
|
|
302
|
-
# 4. 监控新 run
|
|
303
|
-
run_id=$(gh run list --workflow=release.yml --limit=1 --json databaseId --jq '.[0].databaseId')
|
|
304
|
-
gh run watch "$run_id" --exit-status
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
⚠️ **commit 前隔离 staging**:如果你边修边并行有其他文件改动,用 `git commit -- <specific-file>` 只 commit 指定文件,避免把不相关改动一起打包到"修复 commit"里污染历史。
|
|
308
|
-
|
|
309
|
-
---
|
|
310
|
-
|
|
311
|
-
## 调试技巧
|
|
312
|
-
|
|
313
|
-
| 场景 | 命令 |
|
|
314
|
-
|---|---|
|
|
315
|
-
| 看**进行中** job 的日志(`gh run view --log` 不让看 in-progress) | `gh api repos/<owner>/<repo>/actions/jobs/<job-id>/logs` |
|
|
316
|
-
| 拿失败 job ID 列表 | `gh run view <run-id> --json jobs --jq '.jobs[] \| select(.conclusion=="failure") \| "\(.databaseId) \| \(.name)"'` |
|
|
317
|
-
| 阻塞等待 run 完成(后台跑) | `gh run watch <run-id> --exit-status` |
|
|
318
|
-
| run 几秒 fail + jobs 为空 | 99% yaml 解析错误,本地跑 `python3 -c "import yaml; yaml.safe_load(...)"` |
|
|
319
|
-
| Filter 失败 job 关键错误 | `gh api .../jobs/<id>/logs \| grep -iE "error\|importerror\|cython\|setuptools\|packaging\|fatal\|not found"` |
|
|
320
|
-
|
|
321
|
-
---
|
|
322
|
-
|
|
323
|
-
## 仓库改名 / 迁移注意
|
|
324
|
-
|
|
325
|
-
GitHub repo 改名后:
|
|
326
|
-
|
|
327
|
-
- ✅ `Settings → Environments`(含 `pypi`)跟着 repo 保留,**不丢**
|
|
328
|
-
- ✅ Trusted Publisher 是绑 `owner/repo` 的,repo 改名后**必须**去 PyPI 改 publisher 配置(`https://pypi.org/manage/account/publishing/` → 找到 publisher → 改 repo 名),否则下次 publish 被 PyPI 拒绝
|
|
329
|
-
- ✅ 旧 repo URL 自动 301 redirect 一段时间,但建议立刻更新本地 `git remote set-url origin <新地址>`
|
|
330
|
-
|
|
331
|
-
---
|
|
332
|
-
|
|
333
|
-
## 不要做
|
|
334
|
-
|
|
335
|
-
- ❌ **不要在本地跑 `python -m build && twine upload`** —— 本地无法 build 跨平台 wheel,且容易绕过 Trusted Publisher 引入凭证管理风险
|
|
336
|
-
- ❌ **不要发 sdist 到 PyPI**(`pypa/gh-action-pypi-publish` 默认只发 wheel,保持这个行为)—— Cython 编译模块的 `.py` 源码会通过 sdist 完整泄漏,破坏混淆意义
|
|
337
|
-
- ❌ **不要假设 GitHub Action 的 `v<N>` major moving tag 一定存在**,先查仓库实际 tag 列表
|
|
338
|
-
- ❌ **workflow 改完不要直接 push**,先 `python3 -c "import yaml; yaml.safe_load(...)"` 本地验证
|
|
339
|
-
- ❌ **不要在 publish 前先发同版本号 sdist 占位** —— PyPI 同版本号占了就废,下次 publish 被拒
|
|
340
|
-
- ❌ **不要在 manylinux2014 容器里期望装上 pyarrow 24+ wheel** —— 必须 manylinux_2_28
|
|
341
|
-
- ❌ **跨平台命令里不要用单引号包参数** —— Windows cmd 不识别
|
|
342
|
-
|
|
343
|
-
---
|
|
344
|
-
|
|
345
|
-
## 违规检测清单
|
|
346
|
-
|
|
347
|
-
| 检查项 | 违规模式 | 严重程度 |
|
|
348
|
-
|--------|---------|---------|
|
|
349
|
-
| build-system 缺 packaging 锁 | 用 PEP 639 license 但 `[build-system].requires` 没列 `packaging>=24.2` | **致命 → 必坏** |
|
|
350
|
-
| YAML 含 `:` 未加引号 | env 值有 `=:all:` / `port:8080` 等冒号但整行未加引号 | **致命 → 必坏** |
|
|
351
|
-
| cibuildwheel 用 major alias | `pypa/cibuildwheel@v3`(不存在)/ `@v2`(不存在) | **致命 → 必坏** |
|
|
352
|
-
| TEST_COMMAND 用 `{project}` | monorepo 子目录 SDK 用 `{project}/tests` | 高 |
|
|
353
|
-
| TEST_COMMAND 外双内单 | `"... -k 'not xxx'"` Windows 必坏 | 高 |
|
|
354
|
-
| manylinux2014 + 大库依赖 | 依赖含 pyarrow/pandas/numpy 但 manylinux 镜像还是 2014 | 高 |
|
|
355
|
-
| 缺 CIBW_BEFORE_TEST binary 锁 | 大型 C 库依赖未预装 binary,源码 build 链失败 | 中 |
|
|
356
|
-
| 用了已退役 runner image | matrix 含 `macos-13` / `macos-12` / `macos-11` / `ubuntu-20.04` 等已下线 label | **致命 → 死等不报错** |
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|