1643 Commits
Author SHA1 Message Date
github-actions[bot]andGitHub 27a97ae63d Merge pull request #584 from yeasy/dependabot/github_actions/dependencies-b2576794da
chore(deps): bump the dependencies group with 2 updates
2026-10-14 21:36:32 +00:00
dependabot[bot]andGitHub f769186bf6 chore(deps): bump the dependencies group with 2 updates
Bumps the dependencies group with 2 updates: [actions/upload-artifact](https://github.com/actions/upload-artifact) and [actions/download-artifact](https://github.com/actions/download-artifact).


Updates `actions/upload-artifact` from 7.0.1 to 7.0.2
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/043fb46d1a93c77aae656e7c1c64a875d1fc6a0a...cf430e030ddbb5b0abf93d22962f4752f3646cd9)

Updates `actions/download-artifact` from 8.0.1 to 8.0.2
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c...9000827ccba6bdab643e8b6fd33ac0654aef8333)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
- dependency-name: actions/download-artifact
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-14 21:33:07 +00:00
922f76b14b chore(deps): bump lodash-es and mermaid (#583)
Bumps [lodash-es](https://github.com/lodash/lodash) and [mermaid](https://github.com/mermaid-js/mermaid). These dependencies needed to be updated together.

Updates `lodash-es` from 4.17.23 to 4.18.1
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

Updates `mermaid` from 12.0.0 to 12.1.0
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@12.0.0...mermaid@12.1.0)

---
updated-dependencies:
- dependency-name: lodash-es
  dependency-version: 4.18.1
  dependency-type: indirect
- dependency-name: mermaid
  dependency-version: 12.1.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
preview-pdf
2026-10-06 19:31:57 -07:00
1a17f64632 chore(deps): bump @mermaid-js/mermaid-cli (#582)
Bumps the dependencies group with 1 update: [@mermaid-js/mermaid-cli](https://github.com/mermaid-js/mermaid-cli).


Updates `@mermaid-js/mermaid-cli` from 11.17.0 to 12.0.0
- [Release notes](https://github.com/mermaid-js/mermaid-cli/releases)
- [Commits](https://github.com/mermaid-js/mermaid-cli/compare/11.17.0...12.0.0)

---
updated-dependencies:
- dependency-name: "@mermaid-js/mermaid-cli"
  dependency-version: 12.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-06 19:27:26 -07:00
Baohua YangandGitHub 40629bef9a Merge pull request #581 from yeasy/dependabot/npm_and_yarn/dependencies-a37a55ca70
chore(deps): bump @mermaid-js/mermaid-cli from 11.16.0 to 11.17.0 in the dependencies group across 1 directory
2026-09-25 19:15:12 -07:00
dependabot[bot]andGitHub 25c17458fc chore(deps): bump @mermaid-js/mermaid-cli
Bumps the dependencies group with 1 update in the / directory: [@mermaid-js/mermaid-cli](https://github.com/mermaid-js/mermaid-cli).


Updates `@mermaid-js/mermaid-cli` from 11.16.0 to 11.17.0
- [Release notes](https://github.com/mermaid-js/mermaid-cli/releases)
- [Commits](https://github.com/mermaid-js/mermaid-cli/compare/11.16.0...11.17.0)

---
updated-dependencies:
- dependency-name: "@mermaid-js/mermaid-cli"
  dependency-version: 11.17.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-16 18:43:40 +00:00
github-actions[bot]andGitHub a7a7ee3eb9 Merge pull request #580 from yeasy/dependabot/github_actions/dependencies-e061d434e4
chore(deps): bump actions/deploy-pages from 5.0.0 to 5.0.1 in the dependencies group
2026-09-09 18:47:05 +00:00
dependabot[bot]andGitHub 45999853f9 chore(deps): bump actions/deploy-pages in the dependencies group
Bumps the dependencies group with 1 update: [actions/deploy-pages](https://github.com/actions/deploy-pages).


Updates `actions/deploy-pages` from 5.0.0 to 5.0.1
- [Release notes](https://github.com/actions/deploy-pages/releases)
- [Commits](https://github.com/actions/deploy-pages/compare/cd2ce8fcbc39b97be8ca5fce6e763baed58fa128...368f82528645a54fb793d4d04e342629a3f51346)

---
updated-dependencies:
- dependency-name: actions/deploy-pages
  dependency-version: 5.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-09 18:44:06 +00:00
yeasy 12c2acca52 fix(命令): 五处照抄跑不起来,另修四处违反本书自己规则的写法
跑不起来的五处:

- 11.5:604 的 Compose 示例里 `db:` 顶在第 0 列,是**顶级键而不是服务**。
  实测 yaml 解析结果是 `{'services': None, 'db': {...}}`,`docker compose up`
  直接报错。同文件其他服务示例都缩进正确。
- 10.3 的 `docker manifest create` 把 `--amend` 当成每个 manifest 的前缀写了
  两遍。upstream `docker/cli` 里它是 `BoolVarP(&opts.amend, "amend", "a", ...)`,
  不取值;用法是 `create MANIFEST_LIST MANIFEST [MANIFEST...]`。照抄不仅参数
  错位,还会打开"修改已有列表"的语义。改为直接跟位置参数。
- 21.7:288 `apk add postgresql-contrib pg-stat-monitor`。核对 Alpine v3.23 的
  APKINDEX(427828 行,直接解包官方索引):这两个包名都**不存在**,Alpine 只
  提供带主版本号的 `postgresql16-contrib` 等;`apk add` 遇到未知包会中止,
  该 Dockerfile 卡在第二步。改为 postgresql16-contrib 并去掉 pg-stat-monitor,
  同时注明原因。
- 19.3:528 用 `pip install --user` 装到 `/root/.local`,运行阶段却 `USER nobody`。
  Debian 的 `/root` 是 0700,nobody 无法进入,容器起来就找不到依赖。改为装到
  `/opt/pydeps`(PYTHONUSERBASE)并同步 COPY 与 PATH。
- 11.8:202 的 `WORDPRESS_REDIS_HOST` 不被官方镜像识别——`wp-config-docker.php`
  只映射固定的一组 WORDPRESS_* 变量,其中没有 Redis 项;Redis Object Cache
  插件读的是 PHP 常量 `WP_REDIS_HOST`。改为经 `WORDPRESS_CONFIG_EXTRA` 注入。

违反本书自己规则的四处:

- 全书 21 处 `FROM ... as`(13 处在正文、8 处在 Dockerfile)用小写 as,会被
  `docker buildx build --check` 的 FromAsCasing 报
  `'as' and 'FROM' keywords' casing do not match`——而本书正是拿 `--check`
  当准绳(7.6:16、10.2:23),且自己另有 12 处已经写成大写 AS。统一为 AS,
  并把 7.17:174 那句"可以使用 `as` 来为某一阶段命名"一并改掉并附上检查项链接。
- 6.3:137 建议"可替换为 `httpd:latest`",而同文件 22 行之后就写着"不要使用
  `latest`,以保证部署的可重复性"。删去该建议。
- 7.6:253 写"多个 ENV 会创建多层",而 4.5:5 明确区分过:只改元数据的指令
  不新增文件系统层。ENV 正属于后者。改为"多出几条构建历史记录"。

核验方式的一点说明:查 Alpine 包是否存在时,我先用网页搜索接口做了一遍,
结果四个包名全部"命中"(包括本不该存在的那个)——那个页面会把查询词回显。
改用官方 APKINDEX 解包比对后才得到可信结论,两种方法交叉确认后才动手。
2026-09-04 15:01:00 -07:00
yeasy 90536e9b85 fix(一致性): 两处相互冲突的建议,其一会让读者悄悄失去本书推荐的默认后端
- 18.1 完整演示了如何开启 `userns-remap`,全程没有任何代价提示。但官方文档
  明写「The containerd image store is not available when using user namespace
  remapping」,而本书基线是 Docker Engine v29.x——该版本新装默认就是
  containerd image store,12.4 节还把它标为「强烈推荐(现代默认)」。照 18.1
  做完,读者会在毫不知情的情况下退回旧存储后端,失去 OCI image index、多架构
  镜像与构建溯源。补上这条互斥关系并交叉引用 12.4。
- 5 章说「生产环境建议指定确切版本号(如 nginx:1.28.0)而非仅大版本号」,
  7 章说「使用主或次版本号而非完整版本号(patch)」并把 patch 版本列入
  「避免」。两条都以全书建议的口吻写成,方向正好相反。它们其实面向不同环节:
  Dockerfile 的**基础镜像**用主/次版本号,才能在重新构建时自动带上安全补丁;
  **部署**一个服务镜像则应钉死确切版本,让部署可复现、升级是显式动作。
  两处各补一句限定范围并互相指引,而不是删掉其中一条。
2026-09-04 14:57:24 -07:00
yeasy 659473e6d8 fix(demo): 三个配套示例目录与其章节漂移,其中两处带真实后果
章节里写着「完整的文件如下」,仓库里配套的实际文件却已经和正文对不上。
这类漂移比正文里的笔误更隐蔽——读者按目录跑起来的是另一份东西。

- `11_compose/demo/django/Dockerfile` 用的是 `ENV PYTHONUNBUFFERED 1` 这种
  空格旧写法,而 7.6 节写着绝对规则:「已被官方弃用……`docker buildx build
  --check` 会直接把它标出来。**一律用等号**」。这是全仓唯一一处违反(已扫过
  全部 md 与 11 个 Dockerfile)。同文件还有三处与 11.6 节不符:`FROM python:3`
  (章节是 3.12-slim)、`pip install` 缺 `--no-cache-dir`(章节专门列表讲了
  它的作用)、requirements 用 psycopg2 而章节表格写着「推荐使用 psycopg 3」。
  按 11.6 同步,现与章节代码块逐字节一致。
- `07_dockerfile/multistage_example/laravel/Dockerfile` 与 7.18 节差三处,
  两处有实质后果:`chmod -R 777 storage`(章节是 `chown www-data` + `775`)
  —— 而 18.5 节正拿 `777` 当"全员读写权限"的风险范例;以及 `COPY composer.json`
  漏掉了 `composer.lock`,正好破坏该章论证的可复现性。第三处 nginx 标签
  1.30 与全书 20 处 1.28 不一致(且章节自己的注释就推荐 1.28-alpine)。
  同步后与章节仅差章节独有的那行说明性注释。
- `11_compose/demo/wordpress/docker-compose.yml` 的 wordpress 服务**完全没有
  `volumes:`**,而 11.8 节专门加粗警告过「`volumes` 配置很重要!没有它,每次
  容器重启数据都会丢失」并声明了 `wp_data`。该 demo 恰好演示了章节警告的那个
  故障。另外还缺 `wp_net`、两个 container_name、`WORDPRESS_DB_NAME` 与
  `uploads.ini` 挂载。按章节同步,并补上章节 11.8.3 已给出内容的 `uploads.ini`
  (否则挂载会指向不存在的文件)。同步后 YAML 实测可解析,volumes 为
  db_data + wp_data。

`secrets/` 目录未补:它被 .gitignore 有意排除,demo 的 README 就是教读者
自己创建的,属于设计而非缺失。
2026-09-04 14:56:23 -07:00
yeasy ac5ec03ab5 fix(安全,版本): seccomp 示例是安全降级,另修三处凭据/暴露面与九处版本过时
安全(四条,前三条都是书自己打自己):

- 18.5 的自定义 seccomp 示例用 `"defaultAction": "SCMP_ACT_ALLOW"` 写了一个
  blocklist。但 `--security-opt seccomp=<file>` 是**整体替换**默认 profile
  而非叠加(官方 seccomp 文档:容器"uses the default profile unless you
  override it"),而默认 profile 恰恰是 deny-by-default 的 allowlist——本节
  第 11 行自己就这么写的。净效果是:挡住了 chmod,却把默认拦下的那 ~44 个
  高危调用(unshare / keyctl / add_key / bpf / perf_event_open / init_module
  / reboot 等)全部放行,**比不加这个参数更危险**,而读者会以为自己加固了。
  改为从官方 default.json 派生,并在示例前置醒目警告。
- 7.6 把 `docker run -e DB_PASSWORD=xxx` 标成「✅ 正确」。它只是把泄漏路径
  从镜像换到了别处:进 shell history、进宿主机进程 argv(同机任何用户 ps
  可见)、并永久固化在 `docker inspect` 的 Config.Env 里。而本书 11.8:102
  明写「不要把数据库密码写入 compose.yaml、.env、**命令行** 或 Git」,
  16.3:251 也要求「避免把真实口令写入命令历史」。改为标注 -e 的泄漏面,
  并给出 --env-file / Compose secrets 的正确做法。
- 6.2 教读者起一个**无认证、无 TLS** 的 registry,且引导把它绑到内网地址、
  开 insecure-registries,全节零安全提示。同网段任意主机可匿名 push,即
  镜像投毒。而 18.3:17 用最强语气写着「绝不能在没有任何安全控制的情况下
  强行开启」。示例改为绑 127.0.0.1,并在两处补上风险说明与指向 6.3 的出口。
- 18.6 的 CI 示例里,Trivy 那条注释写着「务必使用不可变的 commit SHA 引用,
  而非可变标签」(引的是 2026-03-19 Trivy Actions 76 个 tag 被劫持事件),
  可同一 workflow 里另外九条 uses 全是可变标签,包括持有凭据的
  docker/login-action。补一段说明点破这一点。(本仓库自己的
  .github/workflows/ 确实全部按 SHA 钉死,已实测确认后才写进书里。)

版本时效(对 GitHub releases / endoflife.date / docs.docker.com 逐条核实):

- 5.1 与 6.3 说 `ubuntu:24.04`「是最新 LTS」——最新 LTS 已是 26.04
  (2026-04-23 发布);而本书 3.1 早就列出了 26.04,属书内不一致。更糟的是
  5.1 建议可替换为 `ubuntu:20.04`,而 3.1:33 正警告 20.04 已不在支持列表。
- 3.1 的支持列表里的 Ubuntu 25.10 已被 Docker 官方安装文档移除(该版本本身
  也已于 2026-07-01 EOL),删去。
- 4.6 拿 `alpine:3.20` 当"锁定版本以确保可重现性"的范例,而 3.20 已于
  2026-04 EOL;改为 3.24。
- 3.2 的「Debian Bullseye 11 将于 2026 年 8 月底结束长期支持」时态已过期。
- kind v0.31.0 → v0.33.0(落后两个小版本);etcd v3.5.29 → v3.5.33。
- 10_buildx/README 推荐「Docker v28 及以上」,而紧接着的段落就在用
  「自 Docker Engine 29 起」立论;改为 v29。
2026-09-04 14:53:09 -07:00
github-actions[bot]andGitHub 6b9feb1c12 Merge pull request #579 from yeasy/dependabot/github_actions/dependencies-6512b1d693
chore(deps): bump softprops/action-gh-release from 3.0.2 to 3.0.3 in the dependencies group
2026-09-02 18:47:06 +00:00
dependabot[bot]andGitHub a98bffc514 chore(deps): bump softprops/action-gh-release in the dependencies group
Bumps the dependencies group with 1 update: [softprops/action-gh-release](https://github.com/softprops/action-gh-release).


Updates `softprops/action-gh-release` from 3.0.2 to 3.0.3
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/3d0d9888cb7fd7b750713d6e236d1fcb99157228...efb35369e0ad2afab669f228072c1b0d510eae64)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 3.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-02 18:44:51 +00:00
yeasy 1499d4e6ef ci: stop pinning the attest action's SHA inside the test that guards it
供应链测试把 action 的 **SHA 按字面钉死**:

    self.assertIn("actions/attest-build-provenance@0f67c3f... # v4.1.1", ...)

Dependabot 升级这个 action 时改的正是被断言的那一行,于是断言必然失败、`build` 红、
PR `mergeStateStatus=BLOCKED`。这是**结构性死锁**:该 action 的任何一次升级都会打挂
钉死它的测试,PR 永远合不进来。**全集群 7 个仓库钉的是同一个 SHA,6 个 PR 同时卡在这里**
(agentic #13、aifbs #8、ai_security #17、blockchain #176、ceg #16、docker_practice #578)。
反证很干净:另外 4 个断言写成 `assertIn("actions/attest-build-provenance@")` 的仓库
(claude_guide / peg / fde / llm_internals)**同一个升级早就合进去了**。

断言真正要守的是「release job 必须用 SHA 钉住这个 action」——**这个意图不需要记住是哪个
SHA**。改为 `assertRegex(..., r"actions/attest-build-provenance@[0-9a-f]{40} # v\d")`:
仍然强制 40 位 SHA + `# vN` 溯源注释,**比那 4 个仓库现用的宽松写法更严**,但升级不再撞墙。
同批把 aifbs / ai_security 里 `dependabot/fetch-metadata@25dd0e3... # v3.1.0` 的同款钉死
一并改掉(未升级该 action,只解除钉死)。

同时在主干上直接升掉 Dependabot 提的两个版本,**SHA 逐个用 GitHub API 解引用核过**
(`browser-actions/setup-chrome` 的 tag 是 annotated tag,要走 `git/tags` 再取
`.object.sha`,直接读 `git/ref/tags` 会拿到 tag 对象的 SHA 而误判):

- `actions/attest-build-provenance` 0f67c3f… v4.1.1 → 4d10147… v4.2.2
- `browser-actions/setup-chrome` 2e1d749… v2.1.2 → 48ad923… v2.2.0(blockchain_guide 已在 2.2.0)

**负向验证**(AGENTS.md 核心约定 4,逐条证明新断言没被改成永真):把 pin 换成浮动 tag
`@v4` → FAIL;保留 SHA 但删掉 `# vN` 注释 → FAIL;还原 → OK。七个仓库各跑一遍,
结果一致。而「换一个合法 SHA 应当通过」这一条由本提交自身证明——pin 从 v4.1.1 换到
v4.2.2,测试全绿。

各仓库 `check_project_rules`、`unittest discover -s tests -p 'test_*.py'`、
`git diff --check` 均通过;docker_practice 另跑 `npm test` 通过。
2026-08-25 14:43:03 -07:00
yeasy f315ef93b3 test: 新增中文排版门禁(中英文空格 + 含中文括号全角)
今天这批 style 提交把两项排版统一到位,但**没有任何东西拦住下一段正文漂回去**:`check_project_rules.py` 看围栏与链接,`check_emphasis.py` 看加粗标记,工作区的 `format_checker.py` 看引号、围栏语言与标题空行——**行内排版此前一道门禁都没有**。漂移到什么程度是有实测的:同集群一本书的中英文边界曾有 45% 未空格、另一本的含中文括号是 50/50,而且都是在**同一个文件内**混用。

两条规则:

- **规则一**:汉字不得与 `[A-Za-z0-9]` 直接相邻。`Harness的定义`、`第4章` 违规。
- **规则二**:括号内容含任何中文时必须用全角。`(用户命名空间映射)` 违规;**纯拉丁内容的半角括号一律合法**——`新建一个层 (Layer)`、`项目(Agent)的经验` 都不报,因为它们的内容里没有中文。

**刻意不检查**中文标点与拉丁字符之间的空格(`(如 UI、监控服务)`)——全集群一致不加,全角标点本身自带视觉留白。

**负向验证**(AGENTS.md 核心约定 4:不可能失败的检查不构成证据):把本书扫改前(`ef262553~1`)的内容原样喂给这道门禁,报出 规则一 **28 处**、规则二 **338 处**;喂当前内容,报 **0 处**。

文件自带 9 个用例,其中 5 个专门钉住「不该报的别报」和「该报的必须报」:纯拉丁括号不报、行内代码/链接目标/裸 URL/HTML 属性/行间公式不报,而 `<span>中文abc</span>` 的**标签之间的文字**要报、`定价 $25/$125 per M tokens` 这种成对的 `$` **不得**被当成公式把中间的正文遮蔽掉、`| <1% | … | >400ms |` 这种表格行**不得**被当成一个 HTML 标签。后两条是扫改时真踩过的坑,掩码写松了会静默漏报。

占位符取私用区字符(既非汉字也非拉丁字符也非数字),且每条掩码正则都排除该区间——否则贪婪的 `\S+` 会把前一条规则刚放下的占位符一起吞掉。
2026-08-24 16:06:29 -07:00
yeasy 7d9ed83d45 style: 中英文空格 36 处(2 文件)
按本书自己的多数体例收口,不是引入新规范;同批在集群 14 本书上做同一件事。

**中英文空格**:改动前 8753 处已空格 : 36 处紧贴(0.4%),改动后紧贴 **0**。只在「汉字 ↔ 拉丁字母/数字」直接相邻处插一个半角空格;不动中文标点与拉丁字符之间、也不动半角括号与中文之间。

脚本用私用区字符遮蔽围栏代码、行内代码、`$$…$$` 公式、链接与图片目标、HTML 标签、裸 URL、引用式链接定义与 YAML front matter 之后再改。

验证:改动前后各自抹掉全部空白与两种圆括号后**逐字节相同**;左右括号总数守恒;逐行比对无一行新增连续多空格;SUMMARY 目录名与文件标题的一致数改动前后不变。`check_project_rules`、`check_emphasis`、根 `format_checker`、`validate_codeblocks`、`scan_secrets` 全绿,测试套失败集合与改动前一致。
2026-08-24 14:29:39 -07:00
yeasy a780127d48 fix(用词): 「其它」统一为「其他」(34 处 / 25 文件)
《现代汉语词典》与《中文技术文档写作风格指南》都以「其他」为规范词形;
本书基线是 90 处「其他」比 35 处「其它」,是集群里遗留最多的一本
(其余各书 0~4 处,blockchain_guide 已于 2026-08-05 统一过 47 处)。

其中 6 处在标题和目录上——`4.6 其它制作镜像的方式`、`第十七章 容器其它生态`、
`18.5 其它安全特性`——标题与 SUMMARY 条目**同批改写以保持一致**:
改动后 196 条目录名与各文件标题仍是 0 处不一致。

**`CHANGELOG.md` 里那一处刻意不动**:它是历史发布记录,改它等于改历史。

改动前后逐字符对称差:只有 34 个「它」变成「他」,无其他字符变化。
`check_project_rules` 202/202、`check_emphasis` 202/202、37 tests OK。
2026-08-24 13:18:05 -07:00
yeasy ef2625531d style: 含中文的括号统一为全角(336 处 / 110 文件)
全书此前是真·五五开:含中文的括号 336 处半角 : 336 处全角,而且**在同一文件
内混用**——`18_security/18.1_kernel_ns.md` 第 11/17/20 行写 `(如 runC)`,
第 31 行写 `(用户命名空间映射)`。这不是体例问题,是它自己跟自己不一致。

集群里其余 13 本都一边倒(llm_internals 5 : 1049、oc_guide 2 : 713、
forward_deployed_engineering_guide 0 : 108),blockchain_guide 已在 2026-08-05
按同一方向统一过 97 处,故取全角,而非另立新规。全角括号前后不留空格,
原先 `概念 (镜像、容器、仓库) 和` 两侧的空格一并收掉。

**纯拉丁内容的半角括号一个没动**——`新建一个层 (Layer)`、`项目 (project)`
保持原样:改动前后「半角且内容纯拉丁」的数量都是 **1163**,而「半角且含中文」
从 **336 变成 0**。

其余验证:改动前后各自抹掉全部空白与圆括号后**逐字节相同**(两侧均 19559 字符);
左右括号总数改动前后都是 399 : 399,证明没有吞括号也没有配错对。
`check_project_rules` 202/202、`check_emphasis` 202/202、37 tests OK、
根 `format_checker` 与 `validate_codeblocks` 干净、`git diff --check` 干净。
2026-08-24 13:13:23 -07:00
yeasy 4a1e7aeaff fix(4.2): Engine 29 起 docker image ls 默认不再列出虚悬镜像
4.2.5 用一条裸 `docker images` 演示 `<none>` 行,但本书 1.1 的版本提示写明
「本书内容及示例基于 Docker Engine v29.x 及以上版本」,而这条输出在 29 上
不可能出现:docker/cli#6574(2025-10-29 合入,随 29.0 发布)把无标签镜像
从默认列表里拿掉了,Engine 29 发行说明写作 "docker image ls no longer shows
untagged images by default if no --all flag is provided",官方 `docker image ls`
参考页的 Description 现在也是 "Untagged (dangling) images are also hidden by
default. Use the -a (--all) flag to show intermediate layers and dangling
images."(该页 Examples 里的老输出反而还没跟上,以 Description 与发行说明为准)。

示例改用本节自己后面就在推荐的 `-f dangling=true`,并把这条默认行为写进导语。
4.2.6 「-a 会显示很多无标签镜像——这些是构建过程中产生的中间层」同步补上虚悬镜像
——`-a` 的官方说明正是 "default hides intermediate and dangling images",两类
都藏,只说中间层会让读者以为虚悬镜像仍在默认列表里。
2026-08-22 23:30:10 -07:00
yeasy 6a5c6631df fix(links): 11 条外链的落点已改名,逐条改到当前规范地址
上一轮深审只查了「有没有死链」,这批全是 200 但被重定向到别处的:目标页仍在,
路径/域名却换了。逐条 curl 追 final URL 后对齐:

- docs.docker.com 的旧 `engine/reference/commandline/` 命令参考已整体搬到
  `reference/cli/docker/`(resources、learning_roadmap、10.2 三处);
  `get-docker/` 搬到 `get-started/get-docker/`。
- `config/labels-custom-metadata/` 搬到 `engine/manage-resources/labels/`。
  best_practices 那句里两个不同的链接文字原本指向同一个 URL,现按新页的两个
  小节分开:Label keys and values / Manage labels on objects(浏览器实读该页
  DOM,两个 id 都在)。
- Fedora 官网域名由 getfedora.org 改为 fedoraproject.org(3.3、20.4 两处)。
- kubernetes.io 的中文站点是 `/zh-cn/`,`/zh/` 是旧前缀。
- Azure 的产品页由 `/services/` 改到 `/products/`。
- 阿里云容器服务改名 ACK:`/product/kubernetes` → `/product/ack`;
  文档站 `help.aliyun.com/product/85222.html` → `help.aliyun.com/zh/ack/`。
- Sonatype 下载页由 `/product/repository-oss-download` 改到
  `/products/sonatype-nexus-oss-download`。
2026-08-22 23:25:25 -07:00
yeasy a6ecb73f67 fix(17): Podman/Buildah/Skopeo 与 Spin 的官方仓库已迁出原组织
四条链接指向的仓库都换了归属,旧地址目前只靠 GitHub 的改名重定向活着:

- Podman、Buildah、Skopeo 已从 `containers` 迁到 2026-04-20 新建的
  `podman-container-tools` 组织。podman.io 首页与 Get Started 页现在
  直接链向 `podman-container-tools/podman`;GitHub API 显示该仓库 32.7k
  星、homepage 为 podman.io、最后推送在 2026-08-21,是活跃主仓。
  注意 `containers` 组织本身还在(`containers/image`、`common`、`storage`
  等仍留在原处),迁走的只有这三个,故只改这三条。
- Spin 已随项目捐给 CNCF 改名 Spin Framework,仓库为 `spinframework/spin`
  (homepage `https://spinframework.dev`),`developer.fermyon.com/spin`
  现在整站 302 到 `spinframework.dev`。

17.5 的「属于 containers 项目生态」一句同步改掉——那句话本身随迁移失效了。
2026-08-22 23:24:20 -07:00
yeasy 31c0f10b89 fix(appendix): 命令速查表落后 Docker 29 CLI 十余条命令
附录三的客户端命令表停留在管理命令引入之前的形态,本书自己讲了整整两章的
`docker buildx`(第 10 章)与 `docker compose`(第 11 章)都不在表里,而全书正文
分别用到 27 次和 127 次;`image`(41 次)、`container`(20 次)、`system`(10 次)、
`manifest`(9 次)、`scout`(5 次)、`init`(3 次)同样缺席。按 docker/cli v29.0.0
的 `cli/command/commands/commands.go`(builder/checkpoint/config/container/context/
image/manifest/network/node/plugin/registry/secret/service/stack/swarm/system/volume)
与官方 CLI 参考页的 Subcommands 表补齐 15 条,保持原有字母序。`trust` 不补——
Engine 29 发行说明明写 "Docker Content Trust was removed from the Docker CLI",
与 2.3 节的叙述一致。

另两处按 v29.0.0 `cli/flags/options.go` 核对后修正:

- TLS 三个默认路径写成了绝对路径 `/.docker/*.pem`,与同一张表第一行
  「`--config` 默认为 `~/.docker`」自相矛盾。源码里默认证书目录就是客户端配置
  目录,应为 `~/.docker/{ca,cert,key}.pem`。
- 全局选项漏了 `-c, --context`。
- `search` 的说明还写着「Docker index」,官方为 "Search Docker Hub for images"。
2026-08-22 23:23:33 -07:00
00d8a87f55 chore(deps): bump mermaid from 11.16.0 to 11.16.1 (#574)
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.16.0 to 11.16.1.
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.16.0...mermaid@11.16.1)

---
updated-dependencies:
- dependency-name: mermaid
  dependency-version: 11.16.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-09 21:01:47 -07:00
c3e1e4a5a6 chore(deps): bump dompurify from 3.4.12 to 3.4.13 (#575)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.12 to 3.4.13.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.12...3.4.13)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-09 21:01:43 -07:00
yeasy 33bca896b5 fix(11.4): COMPOSE_DEBUG=1 不是 Compose 的环境变量
11.4.2 让读者在 V2 里改用 `COMPOSE_DEBUG=1` 打开调试输出,但这个变量既不在官方
[Compose 环境变量清单](https://docs.docker.com/compose/how-tos/environment-variables/envvars/)
里(该页只有 COMPOSE_PROJECT_NAME、COMPOSE_FILE、COMPOSE_PROFILES … COMPOSE_PROGRESS
等 15 个),在 docker/compose 源码里也搜不到——照做不会有任何效果。

同时 `--verbose` 的定性收紧一档:源码 cmd/compose/compose.go 里它仍会
`logrus.SetLevel(logrus.TraceLevel)`,只是被 `MarkHidden("verbose")` 从 --help 里
藏起来了,并没有像 `--no-ansi` 那样被标 DEPRECATED,所以说"已弃用"不准确。

另:同节下一行的 `-v, --version` 经核实是对的,虽然官方文档的 global options 表里
没有它,但源码 `c.Flags().BoolVarP(&version, "version", "v", ...)` 确实注册了该短选项
(同样是 MarkHidden 才没进文档),故**未改动**。
2026-08-07 23:29:17 -07:00
yeasy 141fdc80bc fix(examples): 修正若干无法按原样执行的命令
* appendix/repo/nodejs.md:`docker build -t my-nodejs-app` 缺上下文参数,实际会
  报 "docker buildx build" requires exactly 1 argument;补 `.`。同文件 docker run
  示例里 `# -v "$ ":/usr/src/myapp \` 这一行的注释把自己的续行反斜杠也注掉了,加上
  紧跟的空行,命令在 `--name my-running-script` 处就结束、没有镜像名,后面几行变成
  独立的无效命令。该行内容与下一行的 --mount 重复,且 `"$ "` 已是残缺文本,删去。

* 06_repository/6.4:`openssl s_client -connect YourDomainName OR HostIP:443` 里的
  占位符带空格,shell 会切成三个参数,-connect 只收到 YourDomainName,openssl 直接
  报错;`docker login YourDomainName OR HostIP` 同理。改为一个变量。

* 08_data/8.2:`docker run -v $(pwd):/app -p 3000:3000 node npm run dev` 跑不起来
  ——官方 node 镜像没有设置 WORKDIR(docker-node 的 Dockerfile 里只有 ENTRYPOINT
  与 CMD),工作目录是 /,npm 找不到 /package.json。补 -w /app,顺手引号包住 $(pwd)
  并给出确定的标签(本书 4.1、7.10 都要求避免 latest)。

* 12_implementation/12.6:整段用的是 iproute2(ip link add / ip netns exec),中间
  却夹了一句 `brctl addif`。bridge-utils 在当前 Debian/Ubuntu/RHEL 默认不再安装,
  照抄会在这一行断掉;改成等价的 `ip link set A master docker0`。

* 14.1/14.2 的 `sysctl --system`、14.1 join 节点的 `systemctl enable/start
  containerd`、6.4 的 `systemctl restart docker` 都缺 sudo,而紧邻的行(sudo tee、
  14.1 第 47 行的 sudo systemctl restart containerd)都带。补齐。

* 15_etcd/demo/cluster/docker-compose.yml 仍留着顶层 `version: "3.6"`,Compose 会
  警告 obsolete;11.1 明写「新文件建议直接省略该字段」,15.3 正文内联的同一份文件
  也早已省略,只有磁盘上的 demo 落下了。删除后 YAML 仍可正常解析。

* 11.4:「对于 web 项目中的一个 db 容器,可能是 web_db」是 Compose V1 的下划线拼接,
  V2 起统一改用连字符,只有 --compatibility 才回到下划线。

* 附录四 CMD 一节四处写成 `CMD ['executable', 'param1']` 单引号,还说「我们建议任何
  服务镜像都使用这种形式」。exec 形式是 JSON,单引号解析不出来会退回 shell 形式,本书
  7.4 就把 `CMD ['node', 'server.js']` 明确标为「❌ 错误:单引号(JSON 不支持)」。
  一并把 `CMD ['PHP', '-a']` 的二进制名改回小写 php。
2026-08-07 23:26:36 -07:00
yeasy 08e981018e fix(content): 修正四处书内自相矛盾的技术断言
1. FROM「必须是第一条指令」(4.5、7.16、summary 三处)与本书 7.7.4「FROM 之前的
   ARG」整节直接冲突。官方 Dockerfile 参考写的是 "A Dockerfile must begin with a
   FROM instruction. This may be after parser directives, comments, and globally
   scoped ARGs",且 "ARG is the only instruction that may precede FROM"。三处
   一并改成「第一条构建指令」并点明例外。

2. 12 章小结把 USER Namespace 列进「默认启用」的一档,并写「容器 root ≠ 宿主机
   root」;而同章 12.2.2 明写 USER Namespace 默认**不**启用、需 userns-remap 显式
   开启,18.1 更直说「在默认情况下,容器内的 root 用户(UID=0)就是宿主机上的
   root 用户」。小结按正文口径改回——这条读反了会直接误判容器逃逸的风险面。

3. 11.5「目前支持三种日志驱动类型」是 Compose 早期文本的残留。官方支持 json-file、
   local、syslog、journald、gelf、fluentd、awslogs、splunk、etwlogs、gcplogs、
   none 共十余种,本书 19 章小结自己就列了六种。改为给出常见取值并链到官方清单。

4. 7.8 与 7.5 的示例注释建议 postgres:latest / redis:latest,而 4.1、7.10、
   7.16、7 章小结、4.5 全都要求避免 latest(7.8 同一文件第 177 行也写「避免
   latest」)。按全书口径改掉这两处。

另:ENV 的空格分隔旧写法(7.6 的「格式一」、附录四的 PG_MAJOR/PATH 示例)改为等号
形式并加注。BuildKit 的 LegacyKeyValueFormat 检查会报
"ENV key=value" should be used instead of legacy "ENV key value" format,
而本书 10.2 与 07 章 README 正是在推荐 docker buildx build --check。
2026-08-07 23:26:12 -07:00
yeasy 42253fc1e8 fix(ci): 示例里的 GitHub Actions 大版本标签落后上游
第 18、21 章的工作流示例仍用 actions/checkout@v6 与 actions/upload-artifact@v4,
而上游当前大版本分别是 v7.0.1(2026-07-20)与 v7.0.1(2026-04-10);本仓库自己的
.github/workflows/* 早已按 SHA 钉在 v7.0.1 上,书里教的和书自己在跑的不一致。
21.2 更是同一个文件内自相矛盾:第 27/108 行写 @v6,第 59 行却钉着 v7.0.0 的 SHA。

sigstore/cosign-installer 一并从 @v3 提到 @v4.1.2:官方 v4.0.0 发布说明写明
"You must upgrade to cosign-installer v4 if you want to install Cosign v3+",
而 18.6 手工安装那段用的是 releases/latest(今天就是 Cosign 3.x),两条路径装的
不是同一个大版本。该仓库没有可移动的 v4 标签,只能引用完整版本号,已就地加注说明。

docker/{build-push,setup-buildx,login}-action、anchore/sbom-action@v0、
github/codeql-action@v3 逐一核对后确认仍是当前受支持版本,未改动;
trivy-action 的 SHA 57a97c7e 经 GitHub API 核对确与 v0.35.0 一致,注释无误。
2026-08-07 23:24:00 -07:00
yeasy b81816bb07 fix(content): 修正已随上游变化失效的版本事实与镜像引用
深审发现 14 处正文与上游现状不符(另有 4 处在 .agent/ 本地记忆,不入库)。
全部经官方一手源核验,并由独立复核者逐条尝试证伪后保留。

**Docker Content Trust 已从 CLI 移除(本书基线正是 Engine 29.x)**
- 18.6:`docker trust`、`DOCKER_CONTENT_TRUST` 的示例此前标为“历史用法”,
  但未说明命令在 29.x 上根本不存在。Engine 29.0 release notes 原文:
  "Docker Content Trust was removed from the Docker CLI";实测 docker/cli
  的 cli/trust 包在 v28.5.0 返回 200、v29.0.0 返回 404
- 2.3、18.6:DCT 不再是“弃用阶段、时间线待公布”——官方 retired 页已给出
  完整时间表(notary.docker.io 2026-12-08 关闭)
- 注意:`--disable-content-trust` 是 MarkDeprecated(隐藏但仍接受为 no-op),
  不是“不可用”。初版提议写成不可用,经复核纠正后才落地

**etcd 维护分支写反了**
- 15_etcd README/15.1/15.2 均称“3.5 已退出维护窗口”,但 2026-07-23 官方与
  v3.7.1、v3.6.14 同批发布了 v3.5.33,公告称三个分支均在维护;3.4 EOL 也应
  是 2026-06(v3.4.45),非 5 月

**其他上游变化**
- 17.6:containerd 首个 2.x LTS 是 2.0(2024-11-05 起),非 2.3
- 6.4:`sonatype/nexus3:3.69` 这个 tag 从不存在(Sonatype 用三段式),改 3.69.0
- appendix/repo/centos:`rockylinux` 官方镜像已冻结(最后推送 2024-05-30),
  改用官方指引的 `rockylinux/rockylinux`(2026-07-12 仍在更新)
- 6.2/6.3:docs.docker.com/registry/ 已 301 到 retired 页,改指
  distribution.github.io(实测 200)
- 6.1:`docker search centos` 的示例输出补上上游现有的 DEPRECATED 标记

**刻意未改**:`minio/minio` 虽在 Docker Hub 标记 archived,但提议替换的
quay.io/minio/minio 最新 tag 停在 2024-07-31,比 Docker Hub 的 2025-09-07
更旧,照改会把读者引向更陈旧的仓库,留待作者定夺。

验证:check_project_rules 202 文件全过、emphasis 全过、40 测试通过、format 干净。
2026-07-28 20:12:47 -07:00
yeasy 0b526c1674 docs: drop the as-of date stamps from prose
A reader dates a book by the book. An inline 截至 <date> or (核验日期:<date>)
tells them nothing they cannot infer, and it decays: this cluster's clearest
case is the eight files that carried 截至 2026-05-18 while git showed they were
written 33 days earlier, one commit having later nudged the date a day for
visual consistency.

Two kinds of date deliberately survive, because they are not as-of stamps:

- Forward-looking deadlines. "Sonnet 5 的 $2/$10 是截至 2026-08-31 的介绍价"
  states when a price expires; deleting that date would lose a fact.
- Ledger metadata. verified_at / expires_at / ttl_days in the volatile-facts
  appendices are inputs to check_volatile_facts(), which fails the build when
  they lapse. That is the right home for a verification date — machine-checked
  and self-expiring, rather than prose nothing re-earns.

Event dates are untouched throughout (2026-06-09 GA, 2026-06-12 暂停). Where a
sentence needed the sequence, "截至 2026-07-09 官方模型页已恢复" became
"官方模型页此后已恢复", which keeps the ordering without the observation date.
2026-07-28 11:38:02 -07:00
yeasy 90ea597376 fix(check-emphasis): recognise code fences opened inside a blockquote
FENCE_RE was `^\s*(`{3,}|~{3,})`, which matches leading whitespace but not
a blockquote marker. A fence written as `> ```python` was therefore never
recognised, so the quoted code was scanned as prose and CJK-adjacent `**`
inside it was reported as non-rendering emphasis. Since the emphasis check
became a blocking CI gate, that false positive could red-line a legitimate
push over a code sample in a callout.

This file documents itself as mirroring check 6 of the workspace-level
format_checker.py, whose FENCE_RE already handles up to three spaces of
indent plus any number of `>` prefixes. Adopting that pattern restores the
stated invariant.

Demonstrated before/after on a file holding the same line both inside and
outside a blockquoted fence: previously only the quoted copy was flagged
(exit 1); now neither is (exit 0), while a genuine prose defect is still
reported (exit 1), so the gate is not weakened. All 14 repos re-checked:
check_emphasis and check_project_rules pass on all 1,615 markdown files,
and the 14 copies remain byte-identical. A mutation-tested regression guard
now lives in the workspace repo at tests/test_blockquote_fence_emphasis.py.
2026-07-24 21:08:44 -07:00
yeasy 21e43e1927 ci: stop && from swallowing a check_project_rules failure
The step ran `check_project_rules.py && check_emphasis.py` with more
commands on following lines. Under `bash -e` the left operand of `&&`
is an exempt context, so a rules failure did NOT abort the step: it
short-circuited past check_emphasis.py, execution continued to the next
line, and the step exited with that last command's status -- green.

Demonstrated:
  bash -e -c 'false_check && emphasis
              next_cmd'          -> exit 0   (failure swallowed)
  bash -e -c 'false_check
              emphasis
              next_cmd'          -> exit 1   (correctly blocks)

So the repo's main content gate could fail while CI stayed green, and
today's new CJK emphasis gate was skipped entirely whenever it did.
Splitting the line restores fail-fast for both.

Swept all 14 repos: only this one and the other CI whose && line is
followed by further commands were affected; the 12 where the && list is
the last line of its run block already exit non-zero and are unchanged.

Verified: YAML parses, both checkers pass, repo test suite unchanged.
2026-07-24 19:05:25 -07:00
yeasy 95ee0aeca5 ci: gate CJK-punctuation bold regressions with a self-contained emphasis check
check_project_rules.py only checks fences + local links, so a bold marker that
CJK punctuation stops from rendering (e.g. `**(配比)**相互交织` — the closer is
preceded by punctuation and followed by a letter, so it can't close and the
asterisks print literally) would pass this repo's own CI. That class of bug had
to be swept out of every book by hand; nothing here was watching for it.

Adds check_emphasis.py — a standalone gate (identical across all books) that
runs CommonMark's flanking + process-emphasis rules and flags markers that
print literally or silently bold the wrong span. The ci.yaml content step now
runs it right after check_project_rules.py. Verified clean on this book.
2026-07-24 07:27:07 -07:00
yeasy 657c0b25df test(html-reader): guard the bold-lead-in list rendering with real pandoc
The reason 640 collapsed lists shipped green is that no test exercised real
markdown->HTML rendering — the existing build_html_reader tests stub pandoc with
a fake, covering TOC/mermaid plumbing but never the markdown parse. This feeds
the exact bold-lead-in pattern through the real pandoc invocation the reader uses
and asserts the list survives as <ul>. Proven both ways: passes with the
extension, fails when it is removed.
2026-07-23 22:57:35 -07:00
yeasy 7391bf95ef fix(html-reader): stop pandoc from collapsing bold-lead-in lists into a paragraph
The book style writes a bold label then a list with no blank line between them:

    **触发分离的条件**:
    - 输入序列长度 > 某阈值

Strict CommonMark (pandoc default -f markdown) requires a blank line before a
list, so it folded every such list into a single <p>. Measured 640 occurrences
across 13 books — troubleshooting checklists and diagnostic steps shipped in the
offline HTML reader as run-on paragraphs, while GitBook, GitHub, and the PDF all
rendered them correctly. The source was fine; only this reader was wrong.

Fix is the additive pandoc extension markdown+lists_without_preceding_blankline,
verified on real book content through real pandoc: the three bullets that
previously joined into one <p> now produce <ul><li>, with no effect on ordinary
paragraphs or on tables (pipe_tables stays enabled).
2026-07-23 22:57:35 -07:00
yeasy 4175760d1b ci: drop the two Dependabot auto-merge steps that could never work
Neither step did what its name said, and together they meant no Dependabot PR
was ever auto-merged — the job died before reaching the merge step.

1. "Confirm required checks are configured" failed OPEN. It read branch
   protection, which GITHUB_TOKEN cannot do (administration scope is not even
   a valid permissions key), so gh api returned 403 JSON into REQUIRED and
   `[ "$REQUIRED" -eq 0 ]` died with "integer expression expected". A failing
   test inside an `if` condition is exempt from set -e, so the guard evaluated
   false and let execution continue. A guard whose whole purpose is refusing
   an unsafe merge silently passed whenever it could not check.

2. "Approve low-risk Dependabot PR" can never succeed: GitHub Actions is not
   permitted to approve pull requests. It is also unnecessary — these repos do
   not require reviews.

The real gate is now branch protection, which as of today requires BOTH
check-commit-identity and CI on every repo, and gh pr merge --auto cannot
complete until they pass. That is stronger than the removed probe, which only
checked that some required check existed.

This makes the 13 repos match oc_guide, which already had exactly this shape
and was the only one whose auto-merge was not broken.

prompt_engineering_guide asserted the old step ORDER; its test now asserts the
invariants that still hold — no self-approve attempt, and auto-merge gated on
the low-risk update-type allowlist.
2026-07-23 17:27:39 -07:00
yeasy b7d4410cf0 fix(build): drop the _site_site workaround that mdPress 0.8.1 made obsolete
The build script anticipated an older mdPress behaviour where a shared
--output made the site land in a sibling directory, then moved it back:

  mdpress build --format site --output _site && rm -rf _site && mv _site_site _site

mdPress 0.8.1 writes straight into --output, so the sibling never appears and
the mv aborts the step:

  ✓ Generated site  → .../docker_practice/_site/index.html
  mv: cannot stat '_site_site': No such file or directory

Caught by a smoke test after 0.8.1 shipped, before any content push fanned it
out — this repo is the only one with a live copy of the workaround (the three
matches in other repos are inside gitignored _book/ build artifacts).

The site itself built fine; only the post-processing dance failed.
2026-07-23 17:14:19 -07:00
yeasy f8691a8868 ci: retry the mdPress PDF build instead of failing the job on a Chrome flake
The mdPress PDF step drives headless Chrome, which intermittently dies with
"websocket url timeout reached" plus dbus/bus.cc:405 errors. Confirmed by the
verbatim signature in six runs across five repos on 2026-07-22 and 07-23 — most
recently it failed a docker_practice smoke test after mdPress had already
finished parsing 196 chapters and assembling the HTML, dying only at step 5/5.
It is infrastructure, not content: a rerun clears it every time.

oc_guide already solved this with tools/build_pdf.py (bounded retries, process-
scoped timeout cleanup, per-attempt logs) and is notably absent from the flake
victims. This applies the same idea to the other 13 repos with a much smaller
change: shadow mdpress with a retry function once per run block, so every call
shape — including the multi-line continuations — is covered without rewriting
42 heterogeneous call sites. `command mdpress` reaches the real binary.

Fails closed: after 3 attempts the function returns 1 and errexit fails the
step. Verified all three paths against a fake mdpress before rollout — first-try
success, success after two flakes, and 3x failure aborting the step without
printing the following command.

oc_guide is untouched (it has no `mdpress build` call). Converging the other 13
onto its richer build_pdf.py remains the better long-term shape.
2026-07-23 17:06:11 -07:00
yeasy eb939f69e5 fix(format): unstick bold markers that CJK punctuation kept from rendering
CommonMark only treats a run of asterisks as an emphasis delimiter when it is
left/right-flanking. CJK punctuation counts as Unicode punctuation, so a closer
like `**数据(配比)**相互交织` is preceded by punctuation and followed by a
letter — it cannot close, and the page shows literal asterisks instead of bold.

Inserts the single space that book-rules 1.1 already asks for, on whichever
side was failing: 2 places across 2 file(s). No wording changed —
every diff here is whitespace-only. Verified against pandoc's gfm reader:
each span now renders as its own <strong>.
2026-07-23 17:04:37 -07:00
yeasy 898fb10bbe ci: track the latest mdPress release instead of a per-repo pinned version
Per the author: mdPress should default to the latest release.

The cluster had drifted to three versions (0.7.10 x4, 0.7.11 x9, 0.7.14 x1),
and nine of those were pinned to bytes upstream had rebuilt under an existing
tag — the exact thing the harness move to 0.7.14 refused to do on principle.
Pinning also took CI down cluster-wide twice when a tag was rebuilt, because
the digest no longer matched. Latest is now 0.7.15; nothing was on it.

Each of the 43 install sites now resolves the newest release at build time by
following the /releases/latest redirect (no api.github.com call, so no
unauthenticated rate limit on shared runner IPs) and exports the version via
GITHUB_ENV.

Integrity is kept, not dropped: the archive is verified against that same
release's published checksums.txt, and a missing entry aborts the step rather
than passing silently. The honest trade-off is that this verifies the download
rather than pinning an immutable artifact — a rebuilt release is now followed
instead of failing the build. That is the intended behaviour here, since the
rebuild breakage was the problem being solved and upstream is the same author.

Tests updated in step: 12 suites asserted the literal MDPRESS_SHA256 as a
proxy for "this download is checksum-verified". They now assert checksums.txt,
which is where that guarantee lives.
2026-07-23 13:47:03 -07:00
yeasy 1e56295251 fix(ci): close two silent false negatives in Identity Guard
An audit found the guard could miss commits it exists to catch.

1. SIGPIPE. Three inner checks used `printf ... | grep -q` under
   `set -euo pipefail`. When grep -q matches early it exits, printf dies of
   SIGPIPE, pipefail turns the pipeline non-zero, the `if` goes false, and the
   offending commit is silently not reported — the guard then prints its
   success line. This file already documents the hazard in a comment and had
   fixed it for the two outer pre-filters; the three inner ones were left as
   pipes. Now here-strings, like the rest.

   Demonstrated on a synthetic revert-style commit (an AI trailer quoted near
   the top, then a 200KB body): the pipe form MISSES it, the here-string form
   detects it. A trailer in the conventional last position happens to survive
   the pipe form, because grep must read to the end before matching — which is
   why this never showed up in practice.

2. `chatgpt` was in BAD_NAME and BAD_ANY but not BAD_TRAILER, so a
   Co-authored-by naming ChatGPT with an email outside anthropic.com/openai.com
   was not caught. Confirmed by test, then added.

Verified before pushing this time: the amended script flags an AI-authored
commit, a large-body early trailer, and a ChatGPT trailer, and scans
claude_guide's full 345-commit history with zero false positives — that repo
has 46 commits with Claude or Anthropic in the subject, none of which are
identity or trailer hits.
2026-07-23 13:13:39 -07:00
yeasy 52f6c73478 ci: bound every workflow job with timeout-minutes
A wedged Chrome/mdPress step held a harness runner for 27 minutes yesterday
(normal run: ~4 min) and ignored `gh run cancel`; it only stopped because that
job happened to carry timeout-minutes: 30. An audit found 66 of the 101 jobs
across the cluster had no timeout at all — the same hang there would have held
a runner for GitHub's 6-hour default.

Values come from measured run history, not guesses. Across ~120 successful
runs the slowest workflow tops out at 10 min (Update Preview Publications),
CI at 6.5, Update Preview PDF at 4.8:

  30 min — jobs that run Chrome/mdPress/pandoc (3x the observed max, and the
           value harness already used)
  15 min — release, publish, deploy, check-link, chaincode-tests
  10 min — dependabot auto-merge

Every value has at least 6x headroom over its job's observed maximum, so this
should never turn a slow-but-working run into a failure.

Verified: all 72 workflow files still parse, and all 101 jobs now carry an
integer timeout in range.
2026-07-23 08:26:48 -07:00
yeasy 7c96d19fd4 fix(links): busybox 官方仓库 pointed at the homepage, duplicating the line above
git.busybox.net is gone — every path under it 404s, including /busybox/ — so
the earlier sweep replaced the dead repo link with https://busybox.net/. That
is byte-identical to the 官网 bullet directly above it, leaving two bullets
with different labels and the same URL, and a 官方仓库 label describing a page
that is not a repository.

Points it at the official source-download index instead and relabels it
源码下载. Verified 200.
2026-07-23 07:40:41 -07:00
yeasy d705dfb631 fix(ci): make the Mermaid fail-closed guard actually fire
The guard added earlier could never trigger. tools/render_mermaid.py has no
nonzero exit path — its own docstring said "Exits 0 even if some/all fail
(non-fatal)" — so `if ! render_mermaid.py ...; then exit 1; fi` never ran its
body, and a build that rendered zero diagrams still passed. Reproduced by
running the script with no Chrome on PATH: it printed the fallback warning and
exited 0.

Adds a --strict flag that exits 1 when Chrome is missing or any diagram fails
to render, and passes it from every workflow invocation. The default stays
lenient so local mobile-reader builds keep working, which is what the exit-0
behaviour was actually for.

Verified both directions: with no Chrome, --strict exits 1 and the default
exits 0; against the real book with Chrome, --strict renders every diagram and
exits 0.
2026-07-23 07:40:28 -07:00
yeasy 1c8f0dc14d chore: remove the remaining VuePress artifacts
Follows the devDependency removal. The build has been mdPress-only since
1.7.0 and nothing referenced these:

- .vuepress/ (config.js + .gitignore). The mirror it configured is still
  live, but no workflow in this repo builds or deploys it, so this was a
  recipe nobody ran. Local .vuepress/dist/ (400 files of Feb-2026 build
  output, never tracked) was removed from the working tree too — with its
  .gitignore gone it would otherwise have shown up as untracked noise.
- docker-compose.yml: the vuepress-offline service, plus the now-unused
  &mdpress-offline anchor that existed only to feed it.
- README: the 'docker run ... :vuepress' one-liner. That tag was last
  pushed 2022-06-25, so readers following it got a four-year-old snapshot
  of the book. 'mdpress serve' immediately above it still works.
- CODEOWNERS, .zhlintignore and check_project_rules.py entries for the
  deleted directory, and the check-link allowlist entry for the mirror
  (no content links to it).

The CHANGELOG entry recording the original CI removal is left alone — it
is history.

Verified: npm test passes (3 + 35), check_project_rules passes 202 files,
docker-compose.yml still parses with no dangling YAML aliases.
2026-07-22 22:51:34 -07:00
yeasy 2c8acc6c9c build(deps): retire the unused VuePress toolchain
The build moved to mdPress in 1.7.0 ('移除遗留的 vuepress 构建,统一使用
mdpress'), but the VuePress devDependencies stayed. Nothing references them:
build/serve/start/pdf all call mdpress, and the only occurrence of vuepress
in code is '.vuepress' inside check_project_rules.py's ignore list. Pages
still ran 'npm ci', so every deploy installed the whole VuePress tree as
dead weight.

They were also the entire source of the repo's 91 Dependabot alerts. The
lockfile carried two copies of several packages; the vulnerable ones were
all VuePress's (dompurify 2.4.3, highlight.js 9.18.5, katex 0.15.6,
mermaid 9.4.3) while mermaid-cli's copies are above every patched
threshold. Removing the three devDependencies drops 16 of the 20 flagged
packages from the tree entirely and leaves the other four at safe versions.

.vuepress/config.js is deliberately kept — the mirror site it configures is
still live, and whether to archive it is a separate decision.

Verified: npm test passes (3 + 35 tests) and check_project_rules passes
202 files.
2026-07-22 20:11:38 -07:00
yeasy a1f63a565b fix(links): repair dead external links found by a cluster-wide sweep
对 13 本书的 1,842 个外链做了一次全量 curl 健康检查(跟随重定向、带浏览器 UA),
逐条排除误报后确认的真死链。替换目标全部逐个验证返回 200。

注意两类**不是**缺陷、因此没有动的:
- platform.claude.com / docs.anthropic.com 当时返回 500,串行重试仍 500,
  但同日早些时候可正常访问——是上游临时故障,不是死链。
- docker_practice 列出的国内 registry 镜像连不上,是因为书里已明确标注
  「已停止服务」「已于 2024 年关闭」,属于故意保留的历史记录。
2026-07-22 16:51:15 -07:00
yeasy a42f94faad ci: add timeout-minutes to Identity Guard
harness_engineering_guide 的 lab/tests/unit/test_project_configuration.py 要求
每个工作流都写 timeout-minutes,我漏了,导致该仓库 CI 变红。

漏检原因值得记一笔:我本地批量跑测试时用的是 `ls tests/test_*.py`,
而 harness 的测试在 lab/tests/ 下、且用 pytest,这个 glob 一个都没匹配到,
于是它被静默报成"OK"。应当按各仓库 ci.yaml 里真正的测试命令来跑。

timeout-minutes 本身也是好实践:避免作业异常时空转到默认上限。
统一给 14 个仓库都加上,与该测试的其余约束(SHA 固定、禁 continue-on-error、
checkout 不持久化凭据)保持一致。
2026-07-22 16:04:50 -07:00
yeasy 38c89065b6 ci: fix Identity Guard trigger — it was never actually running
上一版给 push 只写了 tags-ignore: ['**'],本意是让发布 tag 不再触发一次
重复的全量审计。但 GitHub 的过滤规则是"只给了 tag 过滤器,就只在 tag 上跑",
因此这条配置的真实含义变成"只跑 tag,但忽略所有 tag" —— 工作流一次都不会触发。

症状很有迷惑性:工作流在 API 里状态是 active、名字也正常显示,
只是 runs 数恒为 0,同一次推送里 CI 和 Preview 都正常跑。
改为 branches: ['**'],覆盖全部分支且天然排除 tag,达到原本的意图。

教训记一句:光看"工作流已注册/active"不能算验证,必须确认它真的产生了 run。
2026-07-22 15:37:50 -07:00