Skip to content

fix(cosh-ng): [rpm] idempotent shells registration and fail-closed erase - #2599

Open
SunnyQjm wants to merge 2 commits into
mainfrom
fix/cosh-2543-rpm-shells-scriptlet
Open

fix(cosh-ng): [rpm] idempotent shells registration and fail-closed erase#2599
SunnyQjm wants to merge 2 commits into
mainfrom
fix/cosh-2543-rpm-shells-scriptlet

Conversation

@SunnyQjm

@SunnyQjm SunnyQjm commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes the two RPM scriptlet gaps tracked in #2543 (ALinux4 default
login-shell readiness audit, PKG-007 / PKG-010):

  • PKG-007: the %post lua scriptlet appended the /etc/shells
    registration with a bare write. When the file lacked a trailing
    newline the entry glued onto the previous line
    (/bin/bash/usr/bin/cosh), and the membership probe never matched
    again, appending another entry on every reinstall.
  • PKG-010: the spec shipped no %preun, so rpm -e cosh-ng
    succeeded (rc=0) while passwd entries still referenced
    /usr/bin/cosh as their login shell, leaving users with a dangling
    shell and no warning.

Changes

  • cosh-ng.spec.in %post: normalize the read content before the
    membership probe (plain find of the exact newline-delimited entry)
    and prepend a separator newline when the existing content lacks a
    trailing one. Registration stays fail-open when /etc/shells
    cannot be opened (unchanged semantics).
  • cosh-ng.spec.in: add a shared %{cosh_replacement_ready}
    predicate and a %preun guard that blocks plain erase ($1 = 0)
    while users still reference /usr/bin/cosh, listing them on stderr.
    Enumeration is fail-closed (failing/empty getent, failing filter
    all abort). The predicate keeps the documented cosh-switch
    (yum swap) path working by allowing removal once /usr/bin/cosh is
    owned by copilot-shell (in a single swap transaction rpm installs
    the new provider before running the old package's %preun, so the
    predicate observes the new owner). A plain erase while users still
    reference the shell stays blocked by design. Upgrades ($1 >= 1)
    are never blocked.
  • tests/test-package-rpm.sh (new packaging harness, mirroring
    tests/test-package-raw.sh): structural lifecycle-section anchors,
    %post install/reinstall matrix through the real RPM Lua
    interpreter (missing file, empty, trailing/missing trailing newline,
    existing/duplicate registrations, substring non-registration),
    %post fail-open behavior, and the %preun guard matrix via
    fixture-backed bash runs (referencing user, no users, broken/empty
    enumeration, broken filter, replacement readiness, swap, upgrade).
  • scripts/run-test-gates.sh: new run_rpm_packaging gate
    (shellcheck + execution) wired into the fast and all gates,
    following the existing run_raw_packaging pattern.

Tests

  • tests/test-package-rpm.sh, run by run_rpm_packaging in
    scripts/run-test-gates.sh fast|all. The RPM-Lua-backed %post
    matrix skips (with an explicit SKIP notice) when the rpm lua
    interpreter is unavailable (macOS dev hosts); the %preun matrix
    and structural anchors always run.

Review follow-up: the scriptlet coverage initially landed in
tests/raw_cli/passthrough.rs; per review it moved to this dedicated
packaging harness, and passthrough.rs is untouched now.

Verification

Focused scope (run, green):

  • FAIL→PASS contrast on the unmodified vs fixed %post scriptlet via
    the real rpm embedded Lua interpreter in an alinux3 container
    (arm64): before — glued /bin/bash/usr/bin/cosh plus duplicate
    growth on reinstall; after — single clean entry, byte-idempotent.
  • %preun acceptance in the same container with a real
    useradd -s /usr/bin/cosh user: erase blocked (rc=1, user listed),
    upgrade allowed, clean erase allowed.
  • bash tests/test-package-rpm.sh green in the alinux3 container
    (full matrix incl. rpm lua) and on the macOS host (skip path for
    the %post matrix); shellcheck tests/test-package-rpm.sh clean.
  • cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, crates/cosh-shell/scripts/check-layout.sh,
    scripts/check-test-inventory.sh — all green after rebase onto
    latest main; additional cargo clippy -p cosh-shell --all-targets
    on rust 1.97.1 clean.

Excluded scope (not run):

  • Full RPM build + rpm -Uvh/rpm -e machine cycle. Scriptlet-level
    acceptance was chosen for this PR; the issue's archived double-arch
    full-package runs are the machine-level reference for the failing
    baseline.
  • cosh-ng cargo test suites (this PR changes no Rust code; covered
    by CI).

Evidence

Hosted on a fork orphan branch (pr-2599-assets), pinned by commit SHA:

Closes #2543

@github-actions github-actions Bot added the component:cosh-ng src/cosh-ng label Aug 17, 2026
@SunnyQjm
SunnyQjm marked this pull request as ready for review August 17, 2026 04:45
@SunnyQjm
SunnyQjm requested a review from KaiLongZhou as a code owner August 17, 2026 04:45
@SunnyQjm

SunnyQjm commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

PR number: #2599
head_sha: 0f968bc
reviewed_at: 2026-08-17T04:59:16Z

Findings

  • [P2] src/cosh-ng/crates/cosh-shell/tests/raw_cli/passthrough.rs:515 — 新增 4 个 packaging scriptlet 测试挂在 raw_cli target,但不驱动 raw CLI(只读 spec 文件、用 rpm --eval 和 bash fixture 回放 scriptlet),与既有 codex P1 评论一致,更适合独立 packaged/spec 测试 target。文件按 diff +343 行估算约从 512 增至 855 行(实测以 CI gate 为准);测试文件阈值以测试 SDD 为准,暂不阻断,但需作者确认归属或迁移计划。
  • [P2] src/cosh-ng/cosh-ng.spec.in:4%preun(约 :102-120)— cosh_replacement_ready 在 scriptlet 内递归调用 rpm -q(事务期 rpmdb 访问历来不被推荐),且宏转义(%%{NAME}\\n)只有真实 spec 构建/事务中才展开;Rust 测试用 bash fixture 完全绕开 rpm 宏展开,PR 也声明排除完整 rpm -e/swap 机器周期。转义或事务期行为不符预期时 fail-closed:普通擦除仍受保护,但文档承诺的 cosh-switch(yum swap)路径可能被静默阻断。建议合入前补一次真实 swap/erase 机器级验证。
  • [P3] passthrough.rspackaged_post_script/packaged_preun_script(约 :564、:578)— 用字符串切片提取 scriptlet,隐式依赖 spec 段落顺序(%preun 须在 %post -p <lua> 之前、%post 后须紧跟 %postun);段落重排会以 panic 而非可读断言失败,结构性锚点测试只钉存在性未钉顺序。

未发现 blocking package/module/public API 组织问题:未新增 crate、root src/*.rs implementation 文件、lib.rs pub mod/pub use、production pubcosh_shell:: self-crate path;未触碰 forbidden dependency direction;未向超 1000 行 production 文件追加功能。spec 修复与 cosh-shell 测试同 PR 属同一能力的必要配套,PR body 已说明关系与验证边界。

Open Questions

  • %post lua 幂等逻辑经矩阵推演正确,但真实 %post -p <lua> 事务上下文与 rpm --eval 是否完全等价,建议以 Linux release gate 实测确认。
  • scriptlet 测试在 /usr/bin/rpm 缺失时静默 skip,实际覆盖依赖 Linux CI 装有 rpm,请确认 gate 环境满足。
  • fail-closed 口径(空 getent 输出即阻断擦除)为 PR 明示的设计取舍,请确认已获 owner 认可。

Validation

  • 未运行(本轮不执行工具):check-layout.shinventory-public-api.sh、cargo 测试、容器内 RPM 验收。
  • 作者已报告:alinux3 容器 FAIL→PASS 对比、cargo test -p cosh-shell --test raw_cli packaged_ 4 passed、fmt/clippy/check-layout/test-inventory 全绿;CI Test cosh-ngTest cosh-ng fast checks 已 SUCCESS。

@qoderai qoderai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

本次审查范围内未发现需要修改的问题。


🤖 Generated by QoderView workflow run

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0f968bcaff

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cosh-ng/crates/cosh-shell/tests/raw_cli/passthrough.rs Outdated
@KaiLongZhou

Copy link
Copy Markdown
Collaborator

Code Review: PR #2599 — fix(cosh-ng): [rpm] idempotent shells registration and fail-closed erase

总体结论:未发现阻塞问题,可以合并

PR 正确修复了 PKG-007 和 PKG-010 两个问题,方案设计合理,测试覆盖充分。以下是详细分析。


PKG-007 修复分析(%post Lua scriptlet)

修复方案正确,覆盖所有边界情况:

场景 旧行为 新行为
/etc/shells 无尾部换行 条目粘连(/bin/bash/usr/bin/cosh 自动补分隔换行 ✅
文件存在但为空 nil concatenation crash or '' 兜底 ✅
子串匹配(/usr/bin/cosh-backup 误判为已注册 true 参数 plain find + 尾部 \n 精确匹配 ✅
重复安装 重复追加 字节幂等 ✅

关键改进点:

  • f:seek('set') 解决 a+ 模式下读位置在文件末尾的问题
  • f:read('*all') or '' 修复了旧代码在空文件时 nil..'\n' 的 crash(旧 bug bonus fix)
  • normalized:find(nl..cosh..nl, 1, true) 使用 plain text 精确匹配,避免了 Lua pattern 的元字符问题

PKG-010 修复分析(%preun guard)

Fail-closed 设计正确:

  • getent 失败 → exit 1 ✅
  • getent 返回空 → exit 1 ✅
  • awk 过滤失败 → exit 1 ✅
  • 用户仍引用 cosh 且 replacement 未就绪 → exit 1 并列出用户 ✅
  • 升级($1 >= 1)→ 不阻塞 ✅
  • copilot-shell 已接管 /usr/bin/cosh → 允许卸载 ✅

%define cosh_replacement_ready 宏设计合理,将 [ -x ... ] 检查和 rpm -q 所有权验证组合为共享谓词。

测试分析

测试质量很高:

  1. packaged_spec_declares_login_shell_lifecycle_sections — 结构性回归保护,防止关键 section 被误删
  2. packaged_post_registers_shell_once_across_shells_variants — 7 个变体矩阵,通过真实 RPM Lua 解释器执行,覆盖文件缺失/空/有尾部换行/无尾部换行/已注册/重复注册/子串等场景
  3. packaged_post_skips_registration_when_shells_cannot_be_opened — 验证 fail-open 语义
  4. packaged_uninstall_blocks_while_passwd_references_cosh — 10 个场景矩阵,使用 bash fixture 模拟 getent/rpm/awk 的各种正常和异常行为

测试正确放在 raw_cli target 下,与 AGENTS.md 的分层规范一致。Linux-only 测试有 #[cfg(target_os = "linux")] 守卫,macOS 开发环境会正确跳过。


P2 观察(非阻塞,供参考)

P2: %postun Lua 脚本存在类似的尾部换行问题(已有问题,非本 PR 引入)

  • 文件与行号src/cosh-ng/cosh-ng.spec.in %postun section
  • 实际问题%postun 的 Lua 代码使用 io.lines() 读取 /etc/shells 并重写。如果文件无尾部换行,最后一行会被 io.lines() 跳过(Lua 行为),导致卸载后丢失该条目。同时 pairs(t) 不保证顺序。
  • 建议:这不是本 PR 引入的问题,但建议后续单独 issue 跟踪并修复,保持 %post%postun 的一致性。

P2: %preun awk pipeline 仅检测 awk 失败,未检测 printf 管道上游失败

  • 文件与行号src/cosh-ng/cosh-ng.spec.in %preun if ! users=$(printf '%s\n' "$passwd_entries" | awk ...)
  • 实际问题:管道退出码取最后一个命令(awk)。printf 几乎不会在管道中失败,但在极端 I/O 错误场景下,printf 失败 + awk 成功会导致 users 为空,guard 被绕过。
  • 建议:可考虑 set -o pipefail,但由于这是 RPM scriptlet 上下文(不一定使用 bash),实际风险极低,不阻塞。

P2: yum swap 场景下 %preun 的行为说明

  • 实际问题:当执行 yum swap cosh-ng copilot-shell 时,RPM 先移除 cosh-ng(%preun 运行),再安装 copilot-shell。此时 /usr/bin/cosh 仍归 cosh-ng 所有,cosh_replacement_ready 谓词为 false。如果有用户仍引用 cosh,swap 会被阻塞。
  • 评估:这是 正确的 fail-closed 行为 — 用户应先迁移 shell 再执行 swap。cosh_replacement_ready 谓词的价值在于支持 copilot-shell 已独立接管 /usr/bin/cosh 的场景。建议在 PR 描述或 commit message 中明确说明这一行为,避免后续维护者困惑。

验证范围

  • ✅ diff 逻辑正确性:手动验证所有边界情况
  • ✅ 测试分层合规(AGENTS.md raw_cli target)
  • %post Lua 语义正确性(seek、read、write、close)
  • %preun shell 逻辑正确性(getent → awk → predicate 链)
  • ⚠️ 未实际运行 cargo test(需要 Linux + RPM 环境)
  • ⚠️ 未验证完整 RPM build + install/erase 机器周期(PR 已在容器中完成)

@SunnyQjm
SunnyQjm force-pushed the fix/cosh-2543-rpm-shells-scriptlet branch from 0f968bc to 569f1f5 Compare August 17, 2026 05:12
@SunnyQjm

SunnyQjm commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

PR number: #2599
head_sha: 569f1f5
reviewed_at: 2026-08-17T05:29:54Z

Findings

  • [P2] src/cosh-ng/cosh-ng.spec.in:4cosh_replacement_readyrpm -q --qf '%%{NAME}}\\n'\\n 转义存在静默失效风险:构建期宏展开只折叠 %%,反斜杠原样保留,运行时 queryformat 拿到 %{NAME}\\n,按 rpm queryformat 规则 \\ 渲染为字面反斜杠,输出为 copilot-shell\n(无真实换行),awk $0 == "copilot-shell" 永不命中,谓词恒 false。方向是 fail-closed(普通擦除仍被保护),但 PR 声明保留的 cosh-switch(yum swap)路径会被静默阻断。bash fixture 中 stub 的 rpm 完全忽略 --qf 参数,无法暴露此问题;PR 也明确排除了真实构建/swap 机器周期。建议合入前用构建产物核验(rpm -qp --scripts 查看展开后 scriptlet 字节,或一次真实 swap/erase 验证),并据此决定 \n\\n
  • [P3] src/cosh-ng/tests/test-package-rpm.sh:92-96(另见 :21-24)— %preun 提取 awk '/^%preun$/{f=1;next} /^%post/{f=0} f' 隐式依赖 section 顺序(%preun 须在 %post 之前;中途插入非 %post* 开头的 section 会被吞进 PREUN);post_script/^%/{f=0} 要求 lua 体永无 % 开头的行。结构性锚点(grep -q '^%preun$' 等)只钉存在性未钉顺序,段落重排会以 bash 语法错误而非可读断言失败暴露。可接受,建议注释说明或锚点补顺序断言。
  • [P3] src/cosh-ng/scripts/run-test-gates.sh:122-131run_rpm_packaging 并入 fast/all 后,fast gate 新增 shellcheck 硬依赖(缺失即失败),与 run_raw_packaging 不做 shellcheck 的处理不对称。CI Test cosh-ng fast checks 已 SUCCESS 说明 CI 镜像满足,但开发机无 shellcheck 时 fast gate 会在测试前失败;请确认这是有意收紧,或对齐 raw gate 策略。

未发现 blocking package/module/public API 组织问题:本 PR 未触碰 crates/cosh-shell/,无新增 crate、workspace 反向依赖、root src/*.rs implementation、lib.rs pub mod/pub use、production pubcosh_shell:: self-crate path,不涉及大文件阈值台账;spec 修复、测试 harness 与 gate 接线属同一打包能力的必要配套。scriptlet 测试按 codex P1 意见从 raw_cli/passthrough.rs 迁出到 tests/test-package-rpm.sh 的处理方向正确,passthrough.rs 已不在 diff 中。%post lua 幂等逻辑经矩阵推演正确:a+seek('set') 修复读到空、plain find 精确匹配避免子串误判、缺尾部换行时补分隔符,install/reinstall 字节级幂等断言充分;%preun fail-closed 枚举(getent 失败/空、awk 失败、owner 异常、不可执行 replacement 均阻断)与升级不阻塞口径和 PR 声明一致;%preun 12 场景 fixture 矩阵与 stub 替换链路核对无误。PR body 过期描述问题在本轮已修复(Review follow-up 段落已说明迁移)。

Open Questions

  • %preun 在 rpm 事务上下文内递归执行 rpm -q 访问 rpmdb,历来不被推荐,不同 rpm 版本行为可能有差异;PR 明确排除了完整 rpm -e/swap 机器周期,建议由 release gate 实测兜底。
  • %post lua 矩阵在 /usr/bin/rpm 缺失时静默 skip,实际覆盖依赖 Linux CI 装有 rpm;当前 Test cosh-ng 仍 IN_PROGRESS,无法确认 CI 上跑的是完整矩阵还是 skip 路径。
  • %post fail-open(/etc/shells 打不开即跳过)与 %preungetent 输出即阻断,均为 PR 明示设计取舍,请确认 owner 已认可。
  • 前序人工评审提到 %postun 存在类似的尾部换行/io.lines() 丢失末行问题(非本 PR 引入);建议单独 issue 跟踪,保持 %post/%postun 一致性。

Validation

  • 未运行(本轮评审不执行工具):check-layout.shcheck-test-inventory.sh、shellcheck、容器内 RPM 验收、真实 spec 构建。
  • 作者已报告:alinux3 容器内 %post FAIL→PASS 字节幂等对比、真实 useradd 驱动 %preun 验收、tests/test-package-rpm.sh 在容器(完整 lua 矩阵)与 macOS(skip 路径)均绿、shellcheck/fmt/clippy/check-layout/check-test-inventory 全绿。
  • CI 信号:Build cosh-ng release SUCCESS、Test cosh-ng fast checks SUCCESS、Test cosh-ng IN_PROGRESS。

The %post lua scriptlet appended the login-shell registration with a
bare write, so an /etc/shells file without a trailing newline glued
the entry to the previous line (/bin/bash/usr/bin/cosh) and the
membership probe never matched again, growing the file on every
reinstall.

Normalize the read content before probing: plain-find the exact
newline-delimited entry, and prepend a separator newline when the
existing content lacks a trailing one. Registration stays fail-open
when /etc/shells cannot be opened.

Cover the install/reinstall matrix (missing file, empty, trailing
newline, missing trailing newline, existing registrations, substring
non-registration) plus the fail-open path with the real RPM Lua
interpreter in a dedicated packaging harness
(tests/test-package-rpm.sh) wired into the fast/all test gates,
skipping the rpm-backed matrix when rpm lua is unavailable.

Fixes: 4ce37f9 ("chore(cosh-ng): add cargo config, nightly rpm build, and rust dev standard")

Assisted-by: Qoder:1.24.2
Signed-off-by: SunnyQjm <mfeng@linux.alibaba.com>
The spec shipped no %preun, so rpm -e cosh-ng succeeded while passwd
entries still referenced %{_bindir}/cosh as their login shell,
leaving those users with a dangling shell and no warning.

Add a %preun guard that blocks plain erase ($1 = 0) and lists the
affected users on stderr. Enumeration stays fail-closed: a failing or
empty getent and a failing passwd filter all abort the erase. The
shared cosh_replacement_ready predicate keeps the documented
cosh-switch swap path working by allowing removal once
%{_bindir}/cosh is owned by copilot-shell. Upgrades ($1 >= 1) are
never blocked.

Extend the packaging harness with fixture-backed bash runs of the
extracted scriptlet across the guard matrix (referencing user, no
users, broken and empty enumeration, broken filter, replacement
readiness, swap, upgrade), pin the predicate's macro expansion to a
real-newline queryformat via rpm --define, and anchor the spec
lifecycle sections and their extraction order structurally.

Fixes: 4ce37f9 ("chore(cosh-ng): add cargo config, nightly rpm build, and rust dev standard")

Assisted-by: Qoder:1.24.2
Signed-off-by: SunnyQjm <mfeng@linux.alibaba.com>
@SunnyQjm
SunnyQjm force-pushed the fix/cosh-2543-rpm-shells-scriptlet branch from 569f1f5 to 3df039e Compare August 17, 2026 05:33
@SunnyQjm

Copy link
Copy Markdown
Collaborator Author

第二轮机器评审与 @KaiLongZhou 评审的逐条回应(head 569f1f5a3df039ef,改动已 fixup 进两个源提交):

1. [P2] cosh_replacement_ready\\n 转义正确性 — 已按建议做低成本实证核验(alinux3 容器,真实 rpm):

  • 宏展开:rpm --define 'cosh_replacement_ready <spec原文>' --eval '%{cosh_replacement_ready}' → 展开为 --qf '%{NAME}\n' -f …\\n 折叠为 \n%%{NAME} 折叠为 %{NAME})✅
  • 运行期:rpm -q --qf '%{NAME}\n' bash | od -cb a s h \n(真实换行);反之 qf 收到 \\n 才会输出字面 \ n
  • 结论:spec 现行 \\n 是正确写法;若按疑虑改成单 \n,宏展开后只剩 n 才是坏的。
  • 加固:tests/test-package-rpm.sh 新增断言——在 rpm 可用环境用 rpm --define 展开谓词并断言其含 --qf '%{NAME}\n' -f,把该宏展开合同钉进 CI,弥补 bash fixture 无法覆盖转义的问题。

2. [P3] scriptlet 提取的段落顺序依赖 — 采纳:结构锚定新增 awk 顺序断言(%preun < %post < %postun),段落重排会以可读断言失败暴露,并补注释说明切片依赖。

3. [P3] fast gate 的 shellcheck 硬依赖不对称 — 与事实不符:既有 run_raw_packaging 有完全相同的守卫(if ! command -v shellcheck …; return 1)并 shellcheck 四个文件,run_rpm_packaging 是同构复制;fast gate 今天就硬依赖 shellcheck,本 PR 未推高环境要求。

4. [P3] PR body 与 diff 漂移 — body 在迁移当时已更新(Changes/Tests 已指向 tests/test-package-rpm.sh);本轮再清理了 Excluded scope 里一处过期的 packaged_ filter 措辞。

5. @KaiLongZhou [P2] %postun io.lines 尾换行问题 — 认同,属存量缺陷(本 PR 未触碰 %postun),建议另建 issue 跟踪,与 %post 口径对齐修复。

6. @KaiLongZhou [P2] printf | awk 管道上游失败 — 认同风险极低的评估:getent 输出此时已成功捕获在内存变量中,printf 重发失败的形态(EPIPE/ENOMEM)在 awk 全量读取下几乎不可达;set -o pipefail 在 scriptlet 的 /bin/sh 上下文不可移植,维持现状。

7. @KaiLongZhou [P2] yum swap 语义说明 — 已在 PR body 补充:单事务 swap 中 rpm 先安装新提供者再跑旧包 %preun,谓词届时观察到 copilot-shell 已接管 → 放行;普通 erase(无替代)在仍有用户引用时按设计阻断,需先迁移用户。

8. Open question:rpm 事务内递归 rpm -q — 已知设计点(只读查询,配合 2>/dev/null 与 fail-closed 兜底:查询失败 → 谓词 false → 阻断而非放行);完整 rpm -e/swap 机器周期按本 PR 声明的边界留给 release gate 实测兜底。

验证:更新后的 harness 在 alinux3 容器(完整路径,含新宏展开断言)与 macOS host(skip 路径)均绿;shellcheck 干净;fmt/clippy/check-layout/check-test-inventory 重跑全绿。

@SunnyQjm

SunnyQjm commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

PR number: #2599
head_sha: 3df039e
reviewed_at: 2026-08-17T07:35:48Z

Findings

  • [P3] src/cosh-ng/tests/test-package-rpm.sh:28-31(锚点段 :12-22)— 第三轮评审项在当前 head 仍未闭环:post_script 的 sed 路径替换(/etc/shells$SHELLS%{_bindir}/cosh$COSH)是静默 no-op 型替换。若未来 spec 重构使 lua 体不再含字面 /etc/shells(如改为变量间接打开),sed 不再生效,run_post 会以真实 rpm lua 在 CI 容器内改写真实 /etc/shells,随后 cmp -s 对不存在的 $SHELLS 返回 2 才暴露——失败方向响亮但有容器副作用且报错有误导性。结构锚点段补一条 grep -q '/etc/shells' "$SPEC"(或对提取结果断言替换已生效)即可一行钉住该耦合。非阻断。

未发现 blocking package/module/public API 组织问题:本 PR 未触碰 crates/cosh-shell/,无新增 crate、workspace 反向依赖、root src/*.rs implementation、lib.rs pub mod/pub use、production pubcosh_shell:: self-crate path,不涉及大文件阈值台账。spec 修复、packaging harness 与 gate 接线属同一 RPM 打包能力的必要配套,边界清晰;scriptlet 覆盖按 codex P1 意见从 raw_cli/passthrough.rs 迁出到独立 harness 的方向正确,passthrough.rs 已不在 diff 中。

逻辑复核(静态推演,与前轮结论一致):%post lua 在 a+seek('set') 修复读到空、or '' 兜底、plain find 双侧换行精确匹配避免子串误判、缺尾换行时补分隔符,install/reinstall 字节幂等;%preun fail-closed 链路(getent 失败/空、awk 失败、replacement 不可执行或属主异常均阻断,upgrade 与已接管 swap 放行)与 12 场景 fixture 矩阵一致;\\n/%%{NAME} 宏转义已经真实 rpm 实证并以 rpm --define … --eval 展开断言钉入 CI。

Open Questions

  • @KaiLongZhou 最新评论(07:30)提出两点前提性质疑,作者尚未回应,建议合入前明确:
    1. 当前 /etc/passwd 注册的是 /usr/bin/cosh-login(缺失时 default 到 bash),dangling-shell 场景对新装用户是否已不存在——若属实,%preun guard 的 $7 == "%{_bindir}/cosh" 匹配仅保护历史遗留状态,请确认这是预期保护范围而非前提失效。
    2. passwd 注册逻辑不在 rpm 包内,擦除期 guard 与包外注册语义存在耦合,请确认 owner 认可该守护归属。
  • (存量问题,非本 PR 引入,评审双方已确认)%postunio.lines() 对无尾换行 /etc/shells 丢末行且 pairs 不保序,建议合入后单独 issue 跟踪并与 %post 口径对齐。
  • rpm 事务内递归 rpm -q 为已知设计点(只读查询 + fail-closed 兜底),完整 rpm -e/swap 机器周期按 PR 声明留 release gate 实测兜底。

Validation

  • 本轮为静态评审,未执行工具:check-layout.shcheck-test-inventory.sh、shellcheck、harness 及容器内 RPM 验收均未运行。
  • 作者已报告:alinux3 容器完整路径(含宏展开断言、无 SKIP 行)与 macOS skip 路径均绿;shellcheck、fmt、clippy、check-layout、check-test-inventory 重跑全绿。
  • CI 当前 head:Build cosh-ng releaseTest cosh-ngTest cosh-ng fast checksPR ChecksCommit Message Lint 全部 SUCCESS。

@SunnyQjm

Copy link
Copy Markdown
Collaborator Author

回应第三轮评审的 open question「CI 是否真正执行了 %post 完整矩阵(非 skip 路径)」——已按建议查 gate 日志实证:

  • Job Test cosh-ng fast checks(run 31998344718 / job 95294099932,head 3df039ef)日志 2026-08-17T05:38:57Zcosh-ng rpm scriptlet tests passed,且全日志无 SKIP: rpm lua interpreter unavailable 行 → rpm lua 守卫通过,%post 七形态 × install/reinstall 矩阵、fail-open 用例与宏展开断言均在 CI 真实执行。
  • 该 runner 镜像自带可用的 rpm(含内嵌 lua);若未来镜像变更导致缺失,harness 会显式打印 SKIP 而非静默,届时可在 gate 日志直接观测。

存量问题 %postun io.lines 丢无尾换行末行(评审双方已确认非本 PR 引入)建议合入后另建 issue 跟踪。

@KaiLongZhou

Copy link
Copy Markdown
Collaborator

@SunnyQjm 这里卸载cosh软件包后,导致默认登录shell残留的问题当前已经不存在了,/etc/passwd里面注册为/usr/bin/cosh-login,如果/usr/bin/cosh不存在,则default到bash。
另外一个维度,rpm包里面并没有注册的逻辑,卸载时候与注册逻辑有些耦合。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P1][cosh-ng] RPM spec:%post /etc/shells 尾换行不幂等 + erase 无 %preun fail-closed(PKG-007/PKG-010)

2 participants