create-dwy 0.19.1 → 0.21.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.
Files changed (113) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/package.json +1 -1
  3. package/src/cli-help.js +31 -2
  4. package/src/sync-all.js +168 -577
  5. package/src/sync-codex.js +42 -28
  6. package/src/sync-cursor.js +0 -25
  7. package/src/sync-global-skills.js +2 -2
  8. package/src/sync-opencode.js +7 -33
  9. package/src/sync.js +59 -226
  10. package/src/tab-multiselect.js +15 -53
  11. package/src/utils.js +0 -1
  12. package/templates/ai-tools/AGENTS.md +223 -0
  13. package/templates/ai-tools/CHANGELOG.md +195 -0
  14. package/templates/ai-tools/VERSION +1 -1
  15. package/templates/ai-tools/hooks/Git/pre-git-commit-ai-signature-check.sh +5 -4
  16. package/templates/ai-tools/rules/Android/dwy-android-core.md +147 -13
  17. package/templates/ai-tools/rules/Android/dwy-android-layering.md +142 -0
  18. package/templates/ai-tools/rules/Android/dwy-android-mvp.md +55 -0
  19. package/templates/ai-tools/rules/Android/dwy-android-support-only.md +2 -0
  20. package/templates/ai-tools/rules/Docker/dwy-docker.md +23 -1
  21. package/templates/ai-tools/rules/Flutter/dwy-flutter-core.md +2 -0
  22. package/templates/ai-tools/rules/Flutter/dwy-flutter-testing.md +1 -1
  23. package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +12 -8
  24. package/templates/ai-tools/rules/Python/dwy-python-backend.md +4 -43
  25. package/templates/ai-tools/rules/Python/dwy-python-layering.md +169 -0
  26. package/templates/ai-tools/rules/Vue/dwy-vue-core.md +11 -49
  27. package/templates/ai-tools/rules/Vue/dwy-vue-layering.md +153 -0
  28. package/templates/ai-tools/rules/iOS/dwy-apple-layering.md +133 -0
  29. package/templates/ai-tools/rules/iOS/dwy-swift-core.md +2 -0
  30. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-code-craft.md +83 -0
  31. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dependency-freshness.md +27 -66
  32. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-git-commit.md +8 -219
  33. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-oss-commercial-license.md +27 -123
  34. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prod-branch.md +31 -0
  35. package/templates/ai-tools/rules//346/225/260/346/215/256/345/272/223/dwy-postgres.md +17 -10
  36. package/templates/ai-tools/rules//346/225/260/346/215/256/345/272/223/dwy-redis.md +6 -3
  37. package/templates/ai-tools/skills/Docker/dwy-deploy-first/SKILL.md +14 -14
  38. package/templates/ai-tools/skills/Docker/dwy-deploy-first/references/chapter-01-mirrors.md +2 -2
  39. package/templates/ai-tools/skills/Docker/dwy-docker/SKILL.md +21 -13
  40. package/templates/ai-tools/skills/Docker/dwy-docker/references/ask-templates.md +13 -12
  41. package/templates/ai-tools/skills/Docker/dwy-docker/references/templates.md +3 -1
  42. package/templates/ai-tools/skills/Docker/dwy-docker/references/version-rules.md +10 -6
  43. package/templates/ai-tools/skills/Docker/dwy-docker/scripts/query_dockerhub.py +21 -6
  44. package/templates/ai-tools/skills/Docker/dwy-docker/scripts/scan_images.sh +2 -2
  45. package/templates/ai-tools/skills/Docker/dwy-mirror-source/SKILL.md +44 -30
  46. package/templates/ai-tools/skills/Docker/dwy-mirror-source/references/dockerfile-snippets.md +2 -0
  47. package/templates/ai-tools/skills/Docker/dwy-mirror-source/references/mirror-providers.md +8 -8
  48. package/templates/ai-tools/skills/Docker/dwy-mirror-source/scripts/apply_mirrors.py +14 -1
  49. package/templates/ai-tools/skills/Docker/dwy-mirror-source/scripts/check_mirrors.py +4 -0
  50. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/SKILL.md +8 -6
  51. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/digest/python-sdk.md +3 -3
  52. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/digest/stream-engine.md +2 -2
  53. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/anti-patterns.md +3 -1
  54. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/change-checklist.md +4 -4
  55. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/lethal-violations.md +5 -5
  56. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/perf-baselines.md +5 -5
  57. package/templates/ai-tools/skills/Python/dwy-eapi/SKILL.md +2 -2
  58. package/templates/ai-tools/skills/Python/dwy-eapi/references/tasks-integration-guide.md +4 -4
  59. package/templates/ai-tools/skills/Python/dwy-eapi/v0/breaking.md +3 -0
  60. package/templates/ai-tools/skills/Python/dwy-eapi/v0/email-canonical.md +28 -3
  61. package/templates/ai-tools/skills/Python/dwy-eapi/v0/index.md +2 -2
  62. package/templates/ai-tools/skills/Python/dwy-eapi/v0/providers-email.md +12 -2
  63. package/templates/ai-tools/skills/Vue/dwy-eui/references/eui-design-guide.md +1 -1
  64. package/templates/ai-tools/skills/Vue/dwy-eui/references/eui-integration-guide.md +12 -0
  65. package/templates/ai-tools/skills/Vue/dwy-eui/shared/config-theme.md +2 -0
  66. package/templates/ai-tools/skills/Vue/dwy-eui/shared/form.md +10 -0
  67. package/templates/ai-tools/skills/Vue/dwy-eui/v2/index.md +5 -0
  68. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +11 -14
  69. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +1 -1
  70. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +1 -1
  71. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-dev/SKILL.md +1 -1
  72. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/SKILL.md +32 -15
  73. 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 +3 -1
  74. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/references/release-workflow-template.yml +8 -5
  75. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/SKILL.md +1 -1
  76. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/assets/appstore-submission.yaml +1 -1
  77. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/aso.md +2 -0
  78. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/in-app-purchase-review.md +1 -1
  79. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +4 -3
  80. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +1 -1
  81. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +1 -1
  82. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +45 -27
  83. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +1 -1
  84. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +1 -1
  85. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/git-tag.md +1 -1
  86. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +1 -1
  87. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +1 -1
  88. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/SKILL.md +8 -0
  89. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/SKILL.md +7 -7
  90. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/references/checks-runtime.md +63 -59
  91. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_base.sh +10 -1
  92. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_capacity.sh +21 -23
  93. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_db.sh +9 -1
  94. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_docker.sh +20 -24
  95. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_logs.sh +3 -3
  96. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/SKILL.md +4 -2
  97. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/references/api.md +1 -1
  98. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/scripts/synthesize.py +1 -0
  99. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/SKILL.md +7 -5
  100. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/references/platform-copy-examples.md +5 -3
  101. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-dependency-choice/SKILL.md +140 -0
  102. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-git-commit/SKILL.md +113 -0
  103. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-shared/SKILL.md +17 -14
  104. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-whiteboard-prototype/SKILL.md +1 -1
  105. package/templates/project/fullstack-monorepo/{CLAUDE.md.hbs → AGENTS.md.hbs} +1 -1
  106. package/src/sync-packs.js +0 -223
  107. package/src/sync-scene.js +0 -178
  108. package/templates/ai-tools/CLAUDE.md +0 -210
  109. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-development.md +0 -115
  110. /package/templates/project/backend/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
  111. /package/templates/project/fullstack-monorepo/backend/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
  112. /package/templates/project/fullstack-monorepo/frontend/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
  113. /package/templates/project/web/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
@@ -2,7 +2,7 @@
2
2
  name: dwy-deploy-first
3
3
  description: >-
4
4
  通用「首次部署」检查清单 skill:按章节推进新服务/新镜像的国内可构建落地。
5
- 当前已开放第 1 章「镜像源」——Dockerfile / 构建环境中的 apt(阿里云)与 uv/pip(阿里云 PyPI);
5
+ 当前已开放第 1 章「镜像源」——Dockerfile / 构建环境中的 apt 与 uv/pip。候选 URL 是先验,当次探测后用延迟最低且内容正确的源;
6
6
  禁止写死具体业务项目名。触发:用户说「首次部署」「第一次上线」「部署第一步」「配置镜像源」
7
7
  「apt 换源」「uv 阿里云」「Docker 构建慢」「国内构建」;或用户执行 /dwy-deploy-first。
8
8
  与 dwy-mirror-source(本机用户级 13 类工具)互补:本 skill 聚焦「项目首次部署构建链路」。
@@ -29,7 +29,7 @@ description: >-
29
29
  ## 总原则
30
30
 
31
31
  1. **通用模板** — 示例用 `python:3.x-slim-bookworm` 等占位;路径/服务名从用户当前项目推断,不写死历史项目。
32
- 2. **默认阿里云** — 国内首次部署默认 `aliyun`;用户明确要求清华/中科大时再改。
32
+ 2. **镜像源当次探测** — 阿里云、清华、中科大是当前候选。在内容正确的源里选本次延迟最低的。用户指定某一家或已有私服时,跳过公网竞速。连通不等于可用。测速做法同 `dwy-mirror-source`。
33
33
  3. **先检查后改** — 扫描现有 `Dockerfile` / `Dockerfile.*` / CI 构建脚本;已合规则静默通过,违规再改。
34
34
  4. **与 dwy-docker 叠加** — 镜像 tag 仍须固定版本;本 skill 不替代版本规范。
35
35
  5. **禁止** `curl https://astral.sh/uv/install.sh` 作为默认装 uv 方式(GitHub 二进制,国内慢/易失败)。
@@ -53,15 +53,15 @@ Alpine / RHEL 系见 `references/chapter-01-mirrors.md`;本章默认以 **Debi
53
53
 
54
54
  | # | 检查项 | 合规 | 违规表现 |
55
55
  |---|--------|------|----------|
56
- | A1 | apt 使用国内源 | `mirrors.aliyun.com`(或团队约定的内网 apt 镜像) | 仍 `deb.debian.org` / `archive.ubuntu.com` / `security.debian.org` 且无替换 |
57
- | A2 | 装 uv 不走 astral.sh 默认 GitHub | `pip install -i https://mirrors.aliyun.com/pypi/simple/ uv` 等 | `curl … astral.sh/uv/install.sh` |
58
- | A3 | uv 索引 | `UV_INDEX_URL` / `UV_DEFAULT_INDEX` = 阿里云 simple | 未设且会访问 `pypi.org` |
59
- | A4 | pip 索引 | `PIP_INDEX_URL` 或 `pip -i` = 阿里云 simple | 默认官方 PyPI 且无 `-i` |
60
- | A5 | trusted-host | 与镜像 host 一致(`mirrors.aliyun.com`) | HTTPS 校验失败或反复警告未处理 |
56
+ | A1 | apt 使用探测通过的国内源或团队私服 | 域名是当次延迟最低且 `Release` 正确的源,或团队内网 apt | 仍 `deb.debian.org` / `archive.ubuntu.com` / `security.debian.org` 且无替换;或写了某个镜像站但没探测 |
57
+ | A2 | 装 uv 不走 astral.sh 默认 GitHub | `pip install -i <当次选出的 PyPI 镜像> uv` | `curl … astral.sh/uv/install.sh` |
58
+ | A3 | uv 索引 | `UV_INDEX_URL` / `UV_DEFAULT_INDEX` = 当次选出的源 | 未设且会访问 `pypi.org` |
59
+ | A4 | pip 索引 | `PIP_INDEX_URL` 或 `pip -i` = 当次选出的源 | 默认官方 PyPI 且无 `-i` |
60
+ | A5 | trusted-host | 与实际使用的镜像 host 一致 | HTTPS 校验失败或反复警告未处理 |
61
61
 
62
62
  **企业内网私服**(域名非公网 mirror):保留私服,不覆盖为阿里云;记录「已使用私服,本章跳过公网镜像」。
63
63
 
64
- ### 1.2 默认 URL(阿里云)
64
+ ### 1.2 当前候选 URL(阿里云,写入前替换成探测赢家)
65
65
 
66
66
  ```text
67
67
  # Debian/Ubuntu apt
@@ -76,7 +76,7 @@ trusted-host: mirrors.aliyun.com
76
76
 
77
77
  ### 1.3 落地动作(Dockerfile 模式)
78
78
 
79
- 按顺序改写(最小 diff;匹配用户现有基础镜像代号):
79
+ 按顺序改写(最小 diff;匹配用户现有基础镜像代号)。下面片段里的阿里云域名是当前示例,落地时换成当次探测选出的源。
80
80
 
81
81
  #### A. apt → 阿里云
82
82
 
@@ -138,17 +138,17 @@ RUN uv sync --frozen
138
138
 
139
139
  1. Dockerfile / 构建脚本中 **无** 未替换的官方 Debian/Ubuntu 默认源(私服除外)
140
140
  2. **无** 默认 `astral.sh/uv/install.sh`(除非用户书面坚持并接受慢/失败风险)
141
- 3. `UV_*` / `PIP_*` 指向 `mirrors.aliyun.com`(或约定私服)
142
- 4. 能完整跑通一次构建(`docker build` 或 `uv sync` / `uv pip install`)且日志显示从阿里云域名拉取
141
+ 3. `UV_*` / `PIP_*` 指向当次选出的源(或约定私服)
142
+ 4. 能完整跑通一次构建(`docker build` 或 `uv sync` / `uv pip install`),日志里的下载域名是这次选中的源
143
143
 
144
144
  输出简短验收表:
145
145
 
146
146
  ```text
147
147
  第 1 章 镜像源
148
- - [ ] apt 阿里云
149
- - [ ] uv/pip 阿里云
148
+ - [ ] apt 为当次延迟最低且内容正确的源(或私服)
149
+ - [ ] uv/pip 为当次延迟最低且内容正确的源(或私服)
150
150
  - [ ] 禁止 astral.sh 默认装 uv
151
- - [ ] 构建试跑通过
151
+ - [ ] 构建试跑通过,日志域名与所选源一致
152
152
  ```
153
153
 
154
154
  ### 1.6 完成后
@@ -1,6 +1,6 @@
1
- # 第 1 章参考:apt / uv / pip 阿里云片段
1
+ # 第 1 章参考:apt / uv / pip 片段
2
2
 
3
- 通用片段,按基础镜像选用。**默认首选阿里云。**
3
+ 通用片段,按基础镜像选用。域名是当前候选示例。写入前按 `dwy-mirror-source` 的当次探测,把域名换成延迟最低且内容正确的源。
4
4
 
5
5
  ---
6
6
 
@@ -11,7 +11,7 @@ description: "Docker 工程规范检查与镜像版本管理。触发场景:
11
11
 
12
12
  **参考文件:** `./references/`
13
13
  - `templates.md` — compose / Dockerfile / dev.sh 模板(用户问"怎么写"时读)
14
- - `version-rules.md` — N-1 minor 选法 + 各类镜像表(选具体版本号时读)
14
+ - `version-rules.md` — N-1 是偏好算法。文中版本表只是格式示例,禁止当推荐版本
15
15
  - `ask-templates.md` — AskUserQuestion 模板(违规需要询问用户时读)
16
16
 
17
17
  ---
@@ -24,7 +24,7 @@ description: "Docker 工程规范检查与镜像版本管理。触发场景:
24
24
  |---|---|
25
25
  | 已合规(tag 固定 / Dockerfile 多阶段 / compose 分离) | **静默放行**,不打扰 |
26
26
  | 违规(latest / 浮动 tag / 单一 compose / 缺 healthcheck 等) | 自动 `AskUserQuestion`,让用户决策 |
27
- | AI 即将新写入(用户让我加 redis 服务) | 先 query_dockerhub.py + AskUserQuestion 选 tag,再写入 |
27
+ | AI 即将新写入(用户让我加 redis 服务) | 先按「版本探测」拿到本次 tag 列表,再 AskUserQuestion,再写入 |
28
28
 
29
29
  **为什么这样设计**:用户写 docker 一半被中断 N 次问"要选哪个版本"会很烦;已经写了 `nginx:1.26.2` 这种合规字段也不该 AI 啰嗦"建议你改成…"。只在违规时才介入。
30
30
 
@@ -111,15 +111,26 @@ AI 看到下列情况之一,立即进入「检查清单」:
111
111
 
112
112
  ---
113
113
 
114
+ ## 版本探测
115
+
116
+ 目标写死:写入的 tag 是本次列表里的具体版本,不是 `latest`、单段 major、代号。偏好 N-1;算出来的 tag 不在列表里、已 EOL、或发布不满 7 天(依赖新鲜度规则)时,丢掉这条偏好,从本次列表重选。
117
+
118
+ `query_dockerhub.py` 只是当前先验,只查 Docker Hub。
119
+
120
+ 1. 镜像在 Hub:跑 `python3 {scripts}/query_dockerhub.py <image>`。选项只能来自这次输出的 tag
121
+ 2. 脚本非 0、404、超时,或镜像不在 Hub(GHCR、Quay、私有仓库):换该镜像真实所在的 registry 或厂商 release 再列 tag。文档里的版本表不能顶上
122
+ 3. 新入口也不成立:继续找还能列出 tag 的来源。都失败才问用户,并说明查不到,禁止用旧数字编选项
123
+ 4. AskUserQuestion 的选项必须是这次看到的 tag(模板见 `references/ask-templates.md`)
124
+
114
125
  ## 交互流程(三种场景)
115
126
 
116
127
  ### 1. 写入前(AI 即将新增 image / Dockerfile / compose)
117
128
 
118
129
  例:用户说"帮我加个 redis"。
119
130
 
120
- 1. `python3 {scripts}/query_dockerhub.py redis` 查推荐版本
121
- 2. AskUserQuestion 让用户选 tag(模板见 `references/ask-templates.md` 模板 1)
122
- 3. 用户选完后用 Edit / Write 写入,按 `references/templates.md` 填补 healthcheck / restart 等字段
131
+ 1. 按「版本探测」拿到本次 tag 列表
132
+ 2. AskUserQuestion 让用户选 tag
133
+ 3. 用户选完后用 Edit / Write 写入,按 `references/templates.md` 填补 healthcheck / restart 等字段。模板里的版本号换成用户选的 tag
123
134
  4. 写入完成后**不再二次询问已写入的合规字段**
124
135
 
125
136
  ### 2. 检查现有文件(用户编辑或粘贴文件后)
@@ -135,7 +146,7 @@ AI 看到下列情况之一,立即进入「检查清单」:
135
146
 
136
147
  1. `bash {scripts}/scan_images.sh <project-path>`
137
148
  2. 收集违规清单
138
- 3. 对每个 `critical` / `high` 违规:query_dockerhub.py 拿推荐 + AskUserQuestion
149
+ 3. 对每个 `critical` / `high` 违规:按「版本探测」拿本次 tag 列表 + AskUserQuestion
139
150
  4. 用户选完批量 Edit
140
151
  5. `low` / `medium` 在最后汇总告诉用户但不强制改
141
152
 
@@ -145,7 +156,7 @@ AI 看到下列情况之一,立即进入「检查清单」:
145
156
 
146
157
  1. **禁止 `latest`** — 任何位置不允许,省略 tag 等同于 `latest`。**为什么**:镜像漂移会让"昨天能跑今天炸",CI 和 prod 跑不一样的内容
147
158
  2. **必须固定 tag** — 必须是具体版本号,禁止浮动 tag(`stable` / `alpine` / `bookworm` 等)
148
- 3. **优先选 N-1 minor** — 当前最新稳定 minor 的前一个 minor 系列最新 patch。**为什么**:避开新版刚发布时未暴露的回归 bug,行业惯例
159
+ 3. **偏好 N-1 minor** — 当前最新稳定 minor 的前一个 minor 系列最新 patch。**为什么**:避开新版刚发布时未暴露的回归 bug。这是偏好,不是写死的版本号;探测结果对不上就重选
149
160
  4. **生产关键服务用 digest** — 数据库、消息队列、网关钉 `@sha256:...`,让 image 100% 不可篡改
150
161
 
151
162
  **完整选法(含 4 类镜像表 + LTS 例外 + 具体例子)**:见 `references/version-rules.md`。
@@ -160,7 +171,7 @@ AI 看到下列情况之一,立即进入「检查清单」:
160
171
  python3 {scripts}/query_dockerhub.py <image> [--namespace library] [--top 20]
161
172
  ```
162
173
 
163
- 输出最近的稳定 tag 列表 + 推荐 N-1 minor。私有命名空间用 `org/image`。
174
+ 输出 Docker Hub 上最近的稳定 tag,并标出 N-1 偏好。私有命名空间用 `org/image`。退出非 0 表示这条先验失效,不是「镜像不存在」。
164
175
 
165
176
  ### `scan_images.sh`
166
177
 
@@ -170,12 +181,9 @@ bash {scripts}/scan_images.sh [project-path]
170
181
 
171
182
  扫描项目所有 Dockerfile / docker-compose / k8s yaml / CI 配置中的镜像引用,输出违规清单。**只读,不改文件**。
172
183
 
173
- ### 降级流程(脚本不可用时)
174
-
175
- `query_dockerhub.py` 失败(私有仓库 / 网络不通):
184
+ ### 先验失效之后
176
185
 
177
- 1. 用 WebFetch 查 DockerHub `https://hub.docker.com/_/<image>/tags` 或官方 release page
178
- 2. 如果仍无法拿到版本列表,**仍然走 AskUserQuestion**,措辞改为"无法自动查询版本,请手动确认",options 提供常见稳定版本号 + "Other (我手动输入)"
186
+ 脚本失败或镜像不在 Hub 时,按「版本探测」换入口。禁止把 `references/version-rules.md` 里的旧版本号放进 AskUserQuestion。查不到列表时让用户手动输入,不提供文档里的「常见稳定版本」。
179
187
 
180
188
  ---
181
189
 
@@ -6,8 +6,9 @@
6
6
 
7
7
  - `question` 必须**指出当前问题**("latest 不符合规范" / "redis:7 只有 major"),而不是泛泛"选个版本"
8
8
  - `header` ≤12 字符,用 `<image> tag` / `多阶段` / `compose 分离` 这类
9
- - 第一个 option 永远是推荐项,标 `(推荐)` / `(N-1 推荐)`
10
- - `description` 必须含**理由**(为什么这个选项),含日期更好
9
+ - 第一个 option 是本次列表里的偏好项,标 `(推荐)` / `(N-1 推荐)`。偏好 tag 不成立时,第一个 option 改为本次列表中的下一个合格 tag
10
+ - `description` 必须含**理由**(为什么这个选项),日期用这次查到的发布日期
11
+ - **禁止**照抄本文件里的 `1.26.2` / `1.27.3`。那些数字只示范选项结构
11
12
  - `preview` 用实际写入文件的那一行(`FROM xxx:tag` / `image: xxx:tag`)
12
13
  - 选项 2-4 个;非生产关键服务不给 digest 选项
13
14
 
@@ -25,19 +26,19 @@ AskUserQuestion({
25
26
  multiSelect: false,
26
27
  options: [
27
28
  {
28
- label: "1.26.2 (N-1 推荐)",
29
- description: "N-1 minor 系列最新 patch,发布 2024-08-28,已过早期回归窗口",
30
- preview: "image: nginx:1.26.2"
29
+ label: "<本次 N-1 tag> (N-1 推荐)",
30
+ description: "本次列表中的 N-1 minor 最新 patch,发布 <本次日期>",
31
+ preview: "image: nginx:<本次 N-1 tag>"
31
32
  },
32
33
  {
33
- label: "1.27.3 (最新稳定)",
34
- description: "最新稳定版,2024-11-26 发布,新功能尝鲜场景再选",
35
- preview: "image: nginx:1.27.3"
34
+ label: "<本次最新稳定 tag> (最新稳定)",
35
+ description: "本次列表中的最新稳定版,发布 <本次日期>,新功能尝鲜场景再选",
36
+ preview: "image: nginx:<本次最新稳定 tag>"
36
37
  },
37
38
  {
38
- label: "1.26.2 + digest (生产关键)",
39
- description: "钉到 digest,image 100% 不可篡改,生产网关推荐",
40
- preview: "image: nginx:1.26.2@sha256:..."
39
+ label: "<本次 N-1 tag> + digest (生产关键)",
40
+ description: "钉到本次查到的 digest,image 不可篡改,生产网关再用",
41
+ preview: "image: nginx:<本次 N-1 tag>@sha256:<本次 digest>"
41
42
  }
42
43
  ]
43
44
  }]
@@ -137,6 +138,6 @@ AskUserQuestion({
137
138
 
138
139
  ## 用户选了 "Other" 自定义版本时
139
140
 
140
- 按用户输入的值写入,但口头提醒一句:"你选的 `xxx:Y.Y.Y` 不在 N-1 推荐范围内(当前推荐 N-1 是 `xxx:Z.Z.Z`),确认是出于具体原因吗?"
141
+ 按用户输入的值写入。若该值不在本次 tag 列表里,先核对它是否真实存在,再提醒一句:"你选的 `xxx:Y.Y.Y` 不是本次列表里的 N-1 偏好(本次偏好是 `xxx:Z.Z.Z`)。" `Z.Z.Z` 必须来自这次探测。
141
142
 
142
143
  不要拒绝写入,但要让用户知道这是非常规选择。
@@ -2,6 +2,8 @@
2
2
 
3
3
  新建 docker 工程或用户问"compose / Dockerfile / dev.sh 怎么写"时读这份文件。
4
4
 
5
+ 文中 `postgres:17.1`、`redis:7.2.6`、`nginx:1.26.2`、`node:20.18.0` 只示范 tag 长什么样。写入项目前换成「版本探测」选出的 tag。
6
+
5
7
  ## docker-compose.dev.yml(开发环境)
6
8
 
7
9
  **核心原则**:快速启动、热更新、方便调试。基础设施容器化,应用代码在宿主机跑(这样 `uvicorn --reload` / `vite dev` 的文件监听才能生效,不用每次改代码都 rebuild 镜像)。
@@ -10,7 +12,7 @@
10
12
  # docker-compose.dev.yml — 只跑基础设施,应用在宿主机跑
11
13
  services:
12
14
  postgres:
13
- image: postgres:17.1 # 固定具体版本,按 query_dockerhub.py 推荐
15
+ image: postgres:17.1 # 格式示例。写入前换成当次探测选出的具体版本
14
16
  ports:
15
17
  - "${PG_PORT:-15432}:5432" # 映射到宿主机非标准端口,避免与其他项目冲突
16
18
  environment:
@@ -1,6 +1,8 @@
1
- # 镜像版本选择规则(N-1 minor)
1
+ # 镜像版本选择(N-1 是偏好)
2
2
 
3
- 选具体版本号时读这份。先用 `scripts/query_dockerhub.py` 查实时数据,再按下面规则推算 N-1。
3
+ 选具体版本号时读这份。版本必须来自当次列出的 tag。`scripts/query_dockerhub.py` 只查 Docker Hub,失败或镜像不在 Hub 时换入口,步骤见 `SKILL.md`「版本探测」。
4
+
5
+ 下面的版本表是格式示例,数字写文档时就过期了。**禁止**把表里的 tag 写进 Dockerfile、compose 或 AskUserQuestion。
4
6
 
5
7
  ## 为什么是 N-1 minor 而不是最新
6
8
 
@@ -12,13 +14,15 @@
12
14
 
13
15
  1. **禁止 `latest`** — FROM / image / containers.image / services.image 任何位置都不允许,省略 tag 等同于 `latest`
14
16
  2. **必须固定 tag** — 必须是具体版本号(如 `17.1`、`1.26.2`、`3.20`),禁止浮动 tag
15
- 3. **优先选 N-1 minor** — 当前最新稳定 minor 的前一个 minor 系列最新 patch
17
+ 3. **偏好 N-1 minor** — 当前最新稳定 minor 的前一个 minor 系列最新 patch。该 tag 不在本次列表、已 EOL、或发布不满 7 天时,从本次列表重选
16
18
  4. **生产关键服务用 digest** — 数据库、消息队列、网关在 tag 后再钉 `@sha256:...`,让 image 100% 不可篡改
17
19
 
18
20
  ---
19
21
 
20
22
  ## 选法分类(按版本号格式)
21
23
 
24
+ 表内数字只说明算法长什么样,不是推荐版本。
25
+
22
26
  ### 1. semver 三段镜像(绝大多数)
23
27
 
24
28
  格式 `MAJOR.MINOR.PATCH`,例如 `nginx:1.27.3`、`redis:7.4.1`。
@@ -28,7 +32,7 @@
28
32
  2. minor 减 1,得到目标 minor 系列
29
33
  3. 取该 minor 系列的最新 patch
30
34
 
31
- | 镜像 | 当前最新稳定 | N-1 minor 选择 | 说明 |
35
+ | 镜像 | 当时的最新稳定(示例) | 当时的 N-1(示例,禁止照抄) | 说明 |
32
36
  |------|-------------|---------------|------|
33
37
  | `nginx` | `1.27.3` | `nginx:1.26.2` | 1.26 系列最新 patch |
34
38
  | `redis` | `7.4.1` | `redis:7.2.6` | 7.4 上一个稳定 minor 是 7.2,跳过仅维护版 |
@@ -40,7 +44,7 @@
40
44
 
41
45
  选法:minor 减 1,取该系列最新 tag。
42
46
 
43
- | 镜像 | 当前最新稳定 | N-1 minor 选择 |
47
+ | 镜像 | 当时的最新稳定(示例) | 当时的 N-1(示例,禁止照抄) |
44
48
  |------|-------------|---------------|
45
49
  | `postgres` | `17.2` | `postgres:17.1` |
46
50
  | `mysql` | `8.4` | `mysql:8.3` |
@@ -52,7 +56,7 @@
52
56
 
53
57
  选法:选**上一个 LTS / stable release**,**不要**用 codename(codename 是浮动 tag,会随 release 漂移),只用版本号。
54
58
 
55
- | 镜像 | 当前最新 LTS | N-1 选择 |
59
+ | 镜像 | 当时的最新 LTS(示例) | 当时的上一个 LTS(示例,禁止照抄) |
56
60
  |------|-------------|---------|
57
61
  | `ubuntu` | `24.04` | `ubuntu:22.04` |
58
62
  | `debian` | `12`(bookworm)| `debian:11`(bullseye)|
@@ -48,11 +48,21 @@ def fetch_tags(namespace: str, image: str, page_size: int = 100, max_pages: int
48
48
  data = json.loads(resp.read().decode("utf-8"))
49
49
  except HTTPError as e:
50
50
  if e.code == 404:
51
- print(f"错误:DockerHub 上找不到 {namespace}/{image}", file=sys.stderr)
51
+ # Hub 404 只说明这条先验失效,镜像可能在 GHCR / Quay / 私有仓库。
52
+ print(
53
+ f"Docker Hub 先验失效:hub.docker.com 上没有 {namespace}/{image}。"
54
+ "不代表镜像不存在。换它所在的 registry 或厂商 release 再列 tag,"
55
+ "禁止用文档里的旧版本号。",
56
+ file=sys.stderr,
57
+ )
52
58
  sys.exit(2)
53
59
  raise
54
60
  except URLError as e:
55
- print(f"错误:访问 DockerHub 失败:{e}", file=sys.stderr)
61
+ print(
62
+ f"Docker Hub 先验失效:访问失败:{e}。"
63
+ "换入口再查,禁止用文档里的旧版本号。",
64
+ file=sys.stderr,
65
+ )
56
66
  sys.exit(3)
57
67
 
58
68
  tags.extend(data.get("results", []))
@@ -130,7 +140,10 @@ def main():
130
140
  })
131
141
 
132
142
  if not pure_version_tags:
133
- print("未找到符合规范的稳定版本 tag。请手动确认。", file=sys.stderr)
143
+ print(
144
+ "Docker Hub 这次没有符合规范的稳定 tag。换入口再列,禁止用文档里的旧版本号。",
145
+ file=sys.stderr,
146
+ )
134
147
  sys.exit(1)
135
148
 
136
149
  # 按 (major, minor) 分组,每组取最大 patch
@@ -172,8 +185,10 @@ def main():
172
185
 
173
186
  if n_minus_1_item:
174
187
  n_major, n_minor, n_patch, _ = n_minus_1_item["version"]
175
- print(f"✅ 推荐 (N-1 minor): {args.image}:{n_minus_1_item['name']}")
176
- print(f" {n_major}.{n_minor} 系列最新 patch,发布于 {format_date(n_minus_1_item['last_updated'])}")
188
+ # 偏好不是落盘结论:tag 须仍在本次列表,且未 EOL、发布满 7 天。
189
+ print(f"偏好 (N-1,须核对仍在本次列表且未 EOL): {args.image}:{n_minus_1_item['name']}")
190
+ print(f" {n_major}.{n_minor} 系列最新 patch,Hub 记录日期 {format_date(n_minus_1_item['last_updated'])}")
191
+ print(" 落盘只用本次输出里的 tag。文档版本表不能代替这次结果。")
177
192
  else:
178
193
  print("⚠️ 仅找到一个 minor 系列,无法推荐 N-1,请手动确认是否可降级 major")
179
194
  print()
@@ -186,7 +201,7 @@ def main():
186
201
  if key == latest_minor:
187
202
  marker = " ← 最新"
188
203
  elif key == n_minus_1_key:
189
- marker = " ← 推荐 (N-1)"
204
+ marker = " ← 偏好 (N-1)"
190
205
  print(f" {item['name']:20s} {format_date(item['last_updated'])}{marker}")
191
206
 
192
207
 
@@ -73,7 +73,7 @@ analyze_image() {
73
73
 
74
74
  # 2. 显式 latest
75
75
  if [ "$tag_part" = "latest" ]; then
76
- record "critical" "$file" "$line" "$raw_line" "禁止使用 latest,改为固定版本如 $image_part:<N-1-minor>(用 query_dockerhub.py 查推荐版本)"
76
+ record "critical" "$file" "$line" "$raw_line" "禁止使用 latest,改为具体版本。版本从当次 tag 列表里选,query_dockerhub.py 只是 Docker Hub 先验"
77
77
  return
78
78
  fi
79
79
 
@@ -175,7 +175,7 @@ echo " high: $count_high (浮动 tag / 单段版本号)"
175
175
  echo " low: $count_low (建议补 digest)"
176
176
  echo ""
177
177
  echo "下一步:"
178
- echo " 1. 用 query_dockerhub.py <image> 查每个违规镜像的推荐版本"
178
+ echo " 1. 按 dwy-docker「版本探测」列每个违规镜像的当次 tag。query_dockerhub.py 失败就换 registry"
179
179
  echo " 2. 用 Edit 工具逐项修复,不要批量替换(不同环境可能用不同 tag 变体)"
180
180
 
181
181
  # 有 critical 或 high 时返回非零,方便 CI 集成
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: dwy-mirror-source
3
- description: "国内镜像源加速配置检查与修复:扫描 pip/uv/poetry/npm/pnpm/yarn/Docker/Go/Cargo/Maven/Gradle/Homebrew/Flutter 等 13 类工具的用户级和项目级配置,识别使用境外默认源或缺失配置的项,提供阿里云/清华/中科大镜像源切换。触发条件:用户说'检查镜像源'、'配置加速'、'换镜像源'、'mirror 检查'、'安装慢'、'下载慢' 时。"
3
+ description: "国内镜像源加速配置检查与修复:扫描 pip/uv/poetry/npm/pnpm/yarn/Docker/Go/Cargo/Maven/Gradle/Homebrew/Flutter 等 13 类工具的用户级和项目级配置,识别使用境外默认源或缺失配置的项。候选 URL 只是当前先验;写入前当次探测,在内容正确的源里选延迟最低的,连通不算通过。触发条件:用户说'检查镜像源'、'配置加速'、'换镜像源'、'mirror 检查'、'安装慢'、'下载慢' 时。"
4
4
  ---
5
5
 
6
6
  # 镜像源加速配置(mirror-source)
@@ -21,19 +21,31 @@ description: "国内镜像源加速配置检查与修复:扫描 pip/uv/poetry/
21
21
 
22
22
  ---
23
23
 
24
- ## 默认首选镜像源
24
+ ## 候选先验,不是赢家
25
25
 
26
- **阿里云**(覆盖最全,BGP 多线,全国质量稳定)。
26
+ `references/mirror-providers.md` 和脚本里的 `MIRRORS` 是当前已知候选。阿里云、清华、中科大、DaoCloud 谁快,随机器和时段变。`preference.json` 的 `preferred_provider` 只表示上次人工指定,不能代替当次测速。
27
27
 
28
- 可通过 `./preference.json` 修改:
28
+ 用户明确指定某一家,或已经是企业私服:跳过公网竞速,沿用指定源。
29
29
 
30
- ```json
31
- {
32
- "preferred_provider": "aliyun"
33
- }
34
- ```
30
+ ### 当次探测
31
+
32
+ 对同一工具的每个候选,发即将真正使用的那一种请求,记从发出到首字节的时间:
33
+
34
+ | 工具 | 探测什么 |
35
+ |------|----------|
36
+ | pip / uv | 索引上的 `/simple/pip/` |
37
+ | apt / apk | 该发行版的 `Release` 或仓库索引 |
38
+ | Docker `registry-mirrors` | `{mirror}/v2/` |
39
+ | gcr / ghcr / quay 等前缀 | `https://{前缀}/v2/` |
40
+ | npm / Go / Cargo / Maven | 该工具实际拉取用的索引 URL |
41
+
42
+ 超时、证书错误、非成功状态、正文里没有目标包或 tag:淘汰。剩下的取本次延迟最低。两者差距在测量噪声内(约 20ms)且已有配置可用时,保持现有配置,避免来回改。
43
+
44
+ Docker daemon 按 `registry-mirrors` 顺序用第一个成功的,所以最快的放第一位,其余只作失败回退。Dockerfile 里的 registry 前缀只能写一个域名,写赢家。
45
+
46
+ 测速结果(URL、毫秒、时间)写在当次说明里,不写回本 skill。候选全部不成立:另找可用源,对新候选重复上述探测。找不到再问用户。
35
47
 
36
- 可选值:`aliyun` / `tsinghua` / `ustc`。详细 URL 表见 `references/mirror-providers.md`。
48
+ `apply_mirrors.py --provider` 只能写三家已知源。赢家属于这三家才把对应 provider 传进去。Docker 顺序用 `--docker-mirrors`,按延迟从低到高逗号分隔,否则脚本会把内置列表排到前面。赢家不在这三家里:按该 URL 改配置,先 diff,用户确认后再写。不要为了迁就脚本改选更慢的已知源。
37
49
 
38
50
  ---
39
51
 
@@ -76,7 +88,7 @@ python3 {scripts}/check_mirrors.py [--scope user|project|both] [--project-path .
76
88
 
77
89
  | 状态 | 含义 | 处理 |
78
90
  |------|------|------|
79
- | ✅ ok | 已用国内推荐源 | 无需操作 |
91
+ | ✅ ok | 已用已知国内源 | 不代表当前最快。用户抱怨慢时重新测速 |
80
92
  | ⚠️ warn | 用了官方默认源(境外) | 提示可切换 |
81
93
  | ⚠️ private | 用了企业私服 | 跳过,不动 |
82
94
  | ❌ missing | 工具已安装但无配置 | 提示创建 |
@@ -121,7 +133,8 @@ python3 {scripts}/apply_mirrors.py [--tools pip,npm,docker] [--scope user]
121
133
  **参数:**
122
134
  - `--tools` — 逗号分隔的工具列表(`pip,uv,npm,pnpm,docker,go,cargo,maven,gradle,brew,flutter`),默认全部
123
135
  - `--scope` — `user` / `project` / `both`,默认 `both`
124
- - `--provider` — `aliyun` / `tsinghua` / `ustc`,默认读 preference.json
136
+ - `--provider` — `aliyun` / `tsinghua` / `ustc`,只在赢家属于这三家时传入。默认读 preference.json,那个值是先验
137
+ - `--docker-mirrors` — 逗号分隔的 https URL,按当次延迟从低到高。只在改 Docker 时传
125
138
  - `--dry-run` — 只输出 diff,不写文件
126
139
  - `--project-path` — 项目级配置的根目录,默认当前目录
127
140
 
@@ -145,13 +158,14 @@ python3 {scripts}/check_mirrors.py --only npm --verbose
145
158
 
146
159
  用户说「检查镜像源」/「配置加速」时:
147
160
 
148
- 1. 跑 `check_mirrors.py` 输出报告
161
+ 1. 跑 `check_mirrors.py` 输出报告。报告里的 provider 是先验,不是赢家
149
162
  2. 把报告里 `warn` / `missing` 项列出来给用户看
150
163
  3. 询问要修复哪些(默认全部 `warn` + `missing`,私服项不动)
151
- 4. 跑 `apply_mirrors.py --dry-run --tools <user-selected>` 输出 diff
152
- 5. 等用户确认 → 跑去掉 `--dry-run` 的版本写入
153
- 6. 修改了 Docker daemon.json → 提示用户**重启 Docker**,本 skill 不自动重启
154
- 7. 修改了 shell rc 文件(Flutter / Homebrew)→ 提示用户 `source ~/.zshrc` 或重开终端
164
+ 4. 对要改的工具按「当次探测」选出延迟最低且内容正确的源
165
+ 5. 跑 `apply_mirrors.py --dry-run --tools <user-selected> --provider <赢家所属的已知源>`。一次命令只有一个 `--provider`,不同工具赢家不同就分开跑。Docker 额外加 `--docker-mirrors <按延迟从低到高的 https URL>`
166
+ 6. 等用户确认 → 加 `--apply` 写入。赢家不在三家已知源里时,不调用脚本硬套,按探测到的 URL 改
167
+ 7. 修改了 Docker daemon.json → 提示用户**重启 Docker**,本 skill 不自动重启
168
+ 8. 修改了 shell rc 文件(Flutter / Homebrew)→ 提示用户 `source ~/.zshrc` 或重开终端
155
169
 
156
170
  **禁止行为:**
157
171
  - ❌ 不询问就修改用户全局配置
@@ -165,7 +179,7 @@ python3 {scripts}/check_mirrors.py --only npm --verbose
165
179
 
166
180
  uv 实际有 **3 个独立的镜像源**,缺一个都会拖慢:
167
181
 
168
- | 层 | 配置项 | 作用 | 国内推荐源 |
182
+ | 层 | 配置项 | 作用 | 当前候选(写入前探测) |
169
183
  |----|--------|------|-----------|
170
184
  | 1. 包索引 | `[[index]]` 或 `UV_DEFAULT_INDEX` | 装 PyPI 包 | `https://mirrors.aliyun.com/pypi/simple/` |
171
185
  | 2. **Python 解释器下载** ⭐ | `python-install-mirror` 或 `UV_PYTHON_INSTALL_MIRROR` | `uv python install 3.12` 时下载解释器 | `https://registry.npmmirror.com/-/binary/python-build-standalone` |
@@ -204,7 +218,7 @@ Docker 镜像加速分**两类,互不替代**:
204
218
  }
205
219
  ```
206
220
 
207
- `apply_mirrors.py --tools docker` 自动配置这一层。
221
+ 这一层用 `--docker-mirrors` 按当次延迟排序后写入。脚本内置的 DaoCloud、中科大顺序只是候选先验。
208
222
 
209
223
  ### 类型 2:gcr / ghcr / quay / k8s 等加速 — 改 image 引用
210
224
 
@@ -242,7 +256,7 @@ FROM ghcr.m.daocloud.io/astral-sh/uv:0.5.0 AS uv
242
256
  FROM gcr.m.daocloud.io/distroless/python3:nonroot
243
257
  ```
244
258
 
245
- **为什么不能自动配置?** 这层加速无法在 daemon.json 实现透明代理,必须显式改 image 字符串。Claude 在用户写 Dockerfile / k8s yaml 时,如发现使用了 gcr/ghcr/quay 等域名,**应主动提示**改前缀(可与 dwy-docker-image skill 协同:固定 tag 时一并替换 registry 域名)。
259
+ **为什么不能自动配置?** 这层加速无法在 daemon.json 实现透明代理,必须显式改 image 字符串。写 Dockerfile / k8s yaml 时若看到 gcr / ghcr / quay 等域名,提示改前缀。前缀从当前候选里当次探测,选延迟最低且 `/v2/` 可用的那一个,不固定写成 DaoCloud。tag 仍按 `dwy-docker` 固定。
246
260
 
247
261
  > 提示:containerd(不是 Docker)支持 `[plugins."io.containerd.grpc.v1.cri".registry.mirrors]` 全局映射;Kubernetes 节点用 containerd 时可写到 `/etc/containerd/config.toml` 实现透明代理。Docker 引擎本身不支持。
248
262
 
@@ -262,14 +276,14 @@ FROM gcr.m.daocloud.io/distroless/python3:nonroot
262
276
 
263
277
  | 检测项 | 严重程度 | 说明 |
264
278
  |--------|---------|------|
265
- | Python pip 用 `pypi.org/simple` 默认源 | warn | 改 `mirrors.aliyun.com/pypi/simple/` |
266
- | uv 缺 `python-install-mirror` | warn | `uv python install` 卡顿,添加 npmmirror 二进制源 |
267
- | uv 用了不稳定 GitHub 代理(如 ghfast/gh-proxy) | warn | 改用更稳定的 npmmirror.com/-/binary |
268
- | npm/pnpm 用 `registry.npmjs.org` | warn | 改 `registry.npmmirror.com` |
269
- | Docker 无 `registry-mirrors` | warn | 添加 `docker.m.daocloud.io` |
270
- | Dockerfile / k8s 用 `gcr.io/...`、`ghcr.io/...`、`registry.k8s.io/...` | warn | 改前缀为 `gcr.m.daocloud.io/...` 等(改 image 字符串) |
271
- | Go 未设 GOPROXY 或为 `direct` | warn | 改 `https://goproxy.cn,direct` |
272
- | Maven 默认 Central | warn | 添加 aliyun mirror |
273
- | Cargo 默认 crates.io | warn | 改 `rsproxy.cn` |
279
+ | Python pip 用 `pypi.org/simple` 默认源 | warn | 当次探测后改到延迟最低的 PyPI 镜像 |
280
+ | uv 缺 `python-install-mirror` | warn | `uv python install` 会去拉解释器,补一个探测通过的二进制源 |
281
+ | uv 用了不稳定 GitHub 代理(如 ghfast/gh-proxy) | warn | 换成探测通过且延迟更低的二进制源 |
282
+ | npm/pnpm 用 `registry.npmjs.org` | warn | 当次探测后改到延迟最低的 npm 镜像 |
283
+ | Docker 无 `registry-mirrors` | warn | 写入当次延迟从低到高的 registry-mirrors |
284
+ | Dockerfile / k8s 用 `gcr.io/...`、`ghcr.io/...`、`registry.k8s.io/...` | warn | 改成探测通过且延迟最低的前缀(改 image 字符串) |
285
+ | Go 未设 GOPROXY 或为 `direct` | warn | 当次探测后改到延迟最低的 GOPROXY |
286
+ | Maven 默认 Central | warn | 当次探测后改到延迟最低的 Maven 镜像 |
287
+ | Cargo 默认 crates.io | warn | 当次探测后改到延迟最低的 crates 镜像 |
274
288
  | 用 npm registry 但没用 https | high | 安全问题,强制换 https |
275
- | 镜像源 URL 已弃用(如 `npm.taobao.org`) | high | 已 EOL,必须迁移到 npmmirror.com |
289
+ | 镜像源 URL 已弃用(如 `npm.taobao.org`) | high | 已 EOL,不能再用。替代地址当次探测后选择 |
@@ -4,6 +4,8 @@ Linux 包管理(apt/apk/yum/dnf)和 Python pip 在 Dockerfile 内的换源
4
4
 
5
5
  ## 通用原则
6
6
 
7
+ 片段里的域名是当前候选示例。写入 Dockerfile 前,按 `SKILL.md`「当次探测」把域名换成延迟最低且内容正确的源。
8
+
7
9
  1. **基础镜像确定后再换源** — 不同 base image 用不同包管理(Ubuntu/Debian 用 apt,Alpine 用 apk,CentOS/RockyLinux 用 yum/dnf)
8
10
  2. **多阶段构建只在需要安装包的阶段换** — 最终运行阶段如果不装包,不必加这些
9
11
  3. **CI/CD 镜像构建可能不需要换源** — 部分 CI 已在境内或自建 mirror,按团队约定
@@ -1,12 +1,12 @@
1
1
  # 镜像源 URL 对照表
2
2
 
3
- 各加速服务的 URL 由 `check_mirrors.py` / `apply_mirrors.py` 中的 `MIRRORS` 字典维护。本文件作为**人类可读的索引**,便于审阅、迁移、扩展新工具。
3
+ 各加速服务的 URL 由 `check_mirrors.py` / `apply_mirrors.py` 中的 `MIRRORS` 字典维护。本文件是**当前候选**,不是指定赢家。写入前按 `SKILL.md`「当次探测」测延迟,在内容正确的源里选最低的。连通不算通过。
4
4
 
5
- 如需更新 URL,请同时改脚本里的 `MIRRORS` 字典和本文档,保持一致。
5
+ 如需更新候选 URL,请同时改脚本里的 `MIRRORS` 字典和本文档,保持一致。候选全部探测失败时,另找可用源,不要从本表挑一个「看起来还行」的。
6
6
 
7
- ## 默认推荐:阿里云
7
+ ## 当前候选:阿里云
8
8
 
9
- 阿里云覆盖最全、BGP 多线、对外网无依赖、稳定性高,故作默认。
9
+ 覆盖面广,常作为先验之一。快慢以当次探测为准。
10
10
 
11
11
  | 工具 | URL | 说明 |
12
12
  |------|-----|------|
@@ -27,7 +27,7 @@
27
27
  | Flutter PUB | `https://pub.flutter-io.cn` | flutter pub |
28
28
  | Flutter STORAGE | `https://storage.flutter-io.cn` | flutter SDK |
29
29
 
30
- ## 备选:清华 TUNA
30
+ ## 当前候选:清华 TUNA
31
31
 
32
32
  教育网友好,混合源最完整。Homebrew 上清华源比阿里维护更新更勤。
33
33
 
@@ -44,7 +44,7 @@
44
44
  | Homebrew bottles | `https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles` |
45
45
  | Flutter PUB | `https://pub.flutter-io.cn`(沿用) |
46
46
 
47
- ## 备选:中科大 USTC
47
+ ## 当前候选:中科大 USTC
48
48
 
49
49
  | 工具 | URL |
50
50
  |------|-----|
@@ -89,7 +89,7 @@ Docker 镜像加速分**两类**,互不替代:
89
89
 
90
90
  | 加速器 | URL | 备注 |
91
91
  |--------|-----|------|
92
- | DaoCloud | `https://docker.m.daocloud.io` | **首选**,免登录、稳定 |
92
+ | DaoCloud | `https://docker.m.daocloud.io` | 当前候选,免登录。是否放第一位由当次延迟决定 |
93
93
  | 中科大 | `https://docker.mirrors.ustc.edu.cn` | 高校镜像 |
94
94
  | 网易 | `https://hub-mirror.c.163.com` | 偶尔波动 |
95
95
  | 阿里云专属 | `https://<your-id>.mirror.aliyuncs.com` | 阿里云用户专属,需登录获取 |
@@ -99,7 +99,7 @@ Docker 镜像加速分**两类**,互不替代:
99
99
 
100
100
  **`registry-mirrors` 字段对 gcr/ghcr/quay/k8s/mcr/nvcr 等其他 registry 完全无效**。要加速这类 image,必须**改 image 引用本身**。
101
101
 
102
- DaoCloud 维护的 11 个 registry 镜像(实测全部可达):
102
+ DaoCloud 维护的 registry 前缀是当前候选。下表是写文档时的对照,用之前对目标前缀探测 `/v2/`,延迟最低且内容正确的才写入:
103
103
 
104
104
  | 源 registry | 镜像域名 | 用途 |
105
105
  |-------------|---------|------|
@@ -13,7 +13,8 @@
13
13
  选项:
14
14
  --tools T1,T2,... 逗号分隔的工具列表(默认全部)
15
15
  --scope {user,project,both} 默认 user(项目级请显式指定)
16
- --provider {aliyun,tsinghua,ustc} 推荐源,默认读 preference.json
16
+ --provider {aliyun,tsinghua,ustc} 已知源之一,默认读 preference.json(先验,不是测速结果)
17
+ --docker-mirrors URL,URL 按当次延迟从低到高覆盖 Docker registry-mirrors 顺序
17
18
  --project-path PATH 默认 cwd
18
19
  --dry-run 只输出 diff,不写文件(默认开启,必须 --apply 才落盘)
19
20
  --apply 显式确认落盘(与 --dry-run 互斥)
@@ -576,6 +577,11 @@ def main():
576
577
  help="逗号分隔的工具列表,默认全部")
577
578
  parser.add_argument("--scope", choices=["user", "project", "both"], default="user")
578
579
  parser.add_argument("--provider", choices=["aliyun", "tsinghua", "ustc"], default=None)
580
+ parser.add_argument(
581
+ "--docker-mirrors",
582
+ default=None,
583
+ help="逗号分隔的 https registry-mirrors,按当次延迟从低到高。传入后覆盖内置 Docker 顺序",
584
+ )
579
585
  parser.add_argument("--project-path", type=Path, default=Path("."))
580
586
  parser.add_argument("--apply", action="store_true",
581
587
  help="实际写入(默认只 dry-run 输出 diff)")
@@ -586,6 +592,13 @@ def main():
586
592
  args = parser.parse_args()
587
593
 
588
594
  provider = args.provider or load_preference()
595
+ if args.docker_mirrors:
596
+ # 内置列表是先验。探测已经给出顺序时,不能再把 DaoCloud 排回第一位。
597
+ ordered = [u.strip() for u in args.docker_mirrors.split(",") if u.strip()]
598
+ if not ordered or any(not u.startswith("https://") for u in ordered):
599
+ print("错误:--docker-mirrors 必须是逗号分隔的 https URL,按延迟从低到高", file=sys.stderr)
600
+ sys.exit(2)
601
+ MIRRORS[provider]["docker"] = ordered
589
602
  project = args.project_path.resolve()
590
603
  only = [t.strip() for t in args.tools.split(",") if t.strip()] if args.tools else []
591
604
 
@@ -853,6 +853,10 @@ def main():
853
853
  else:
854
854
  text, by_status = render_text(results)
855
855
  print(text)
856
+ # provider 来自 preference 或参数,只是候选先验,不能当成测速赢家。
857
+ print()
858
+ print(f"候选先验: {provider}。这不是赢家。")
859
+ print("写入前对同一工具的候选做当次探测:内容正确者中取延迟最低。连通或域名匹配不算通过。")
856
860
 
857
861
  # 退出码:有 warn / missing / deprecated → 1;error → 2;否则 0
858
862
  has_error = any(r.status == "error" for r in results)