Files
johnQ c74fe71f0a feat(slark-dsh-cicd): 新增 DSH/Slark 发布实战技能(REQ-20261007-0003)
- 新建 skills-dev/slark-dsh-cicd-plugin:plugin.json + skills/SKILL.md +
  skills/references/fork-release-and-post-checks.md
- 覆盖两条发布线(DSH 插件 pack 双资产 / Slark 宿主重打包+签名)、权限与身份三连
  (push:true 才可写,ls-remote 通 ≠ 能 push)、无上游写权限的 fork 发布且原仓库零改动、
  干净单提交 + git diff --stat <OURS> 树一致性 gate、改指向后必须重新 pack、
  draft→publish 与「更新检查看不到新版」的因果、GitHub HTTPS/fetch 卡死/无 macOS timeout
  的判死处置、发布后 SHA 逐项核对(verify-remote 仅 Windows)、app.asar 完整性约束、
  ai-proj staging→released 与 /req done 证据要求及文档接口故障绕过
- 与 slark-cicd 明确分工并交叉引用,不重复 staging/生产/Desktop 细节
- update-plugin-meta.py PLUGIN_MAP 增加 slark-dsh-cicd-plugin (skill/dev)
- generate-marketplace.py devops 分类补充 slark-dsh-cicd;marketplace.json 仅新增该条目
- 校验:claude plugin validate . 通过(警告为该仓库既有 install_name 字段提示)、git diff --check 干净
2026-10-07 17:06:36 +08:00

14 KiB
Raw Permalink Blame History

name, description
name description
slark-dsh-cicd DSH 插件包与 Slark 宿主补丁的发布 runbook。覆盖 npm run pack 双 tgz 双资产、无上游写权限时的 fork 发布、干净单提交与树一致性 gate、改指向后必须重新打包、draft→publish 节奏、GitHub HTTPS/SSH 与 fetch 卡死处置、发布后 SHA 核对、app.asar 完整性约束、ai-proj 发布状态对齐。当用户要发布或升级 DSH 插件(dsh-worktable 等)、发布/更新 Slark 宿主补丁、push 或下载 GitHub 失败、插件「检查更新」看不到新版本、需要 fork 发布、或排查 asar 完整性/签名问题时使用。

DSH 插件与 Slark 宿主发布 runbook (slark-dsh-cicd)

面向「把插件包/宿主补丁真正发出去」的可执行清单。默认在发布机执行;命令按阶段照做, 每阶段结束先核验再进入下一阶段。

适用与分工

两条发布线,互不依赖,不要串流程:

发布线 产物 入口 权威源
A · DSH 插件 dist/v<ver>/dsh-worktable.tgz + dsh-worktable-<ver>.tgz(同一验收包) <pkg>/npm run pack GitHub Release(tag = v<包内版本>)
B · Slark 宿主补丁 重打包 + 签名的 Slark.app(含内嵌 DSH runtime) 宿主自身打包/签名链 宿主发布链,不经 GitHub Release

与同市场技能的分工,避免重复:

  • 本技能:DSH 插件包发布、宿主补丁打包侧的完整性约束、发布链本身(权限、tag、Release、SHA 核对)。
  • slark-cicd:Slark 仓库 staging 验收、生产发布、Desktop 安装包上传 OSS、PR/CI 门禁——服务器与包分发细节以它为准。
  • dev-cicd:通用流水线设计与排查(Gitea Actions/缓存/并发)。
  • ai-proj-cicd-release:AI-Proj 服务自身的门禁与回滚。

铁律(先记死)

  1. 打包只走唯一入口:npm run pack(或仓库自带的等价脚本)。手写 tar/npm pack 会绕过结构断言与安装导入验证。 脚本自己会做:版本一致性 → 兼容回归 → 构建 → 语法检查 → 结构断言(精确文件清单、无 src/、无 .map)→ 独立临时目录真实安装 + import() 断言 → 客户端/宿主门禁 → 双资产同源 → SHA-256。
  2. package.json 与 dsh.plugin.json 的版本必须相等,否则构建与打包脚本 fail-closed。发版只升这一处对,不要只改一边。
  3. 产物出了包就不再改:任何源码/常量/文档改动都可能改变 bundle → 旧 .tgz 立即作废、sha 必变。改完必须重新 pack 并重新记录 sha。
  4. 对上游仓库零改动是硬指标(尤其是没有写权限的外部作者仓库):不 push、不打 tag、不建 Release/PR,一次都不做。
  5. 公开发布必须是 draft 起步,对外动作经用户过目后再 --draft=false。
  6. force push 只允许发生在自己 fork 的分支/tag 上;绝不对上游使用 -f。
  7. 发布用的分支必须是干净单提交,不要为了省事把本地历史推上去(本地历史可能含个人路径、凭据、临时调试 commit)。
  8. "下载通了" ≠ "hash 对了":发布完成的判据是「远端资产 sha == 本地验收 sha」,不是页面能打开。

阶段 0:身份与权限三连(先判定能力,再决定链路)

gh auth status                                   # 登录账号是否符合预期
ssh -T git@github.com 2>&1 | head -3             # SSH 身份(回显 Hi <user>! 即通)
gh api repos/<owner>/<repo> --jq '.permissions'  # 关键:{"pull":true,"push":<bool>}
  • 只有 push:true 才能走「上游直发」;push:false 时不要硬撞,直接走阶段 2 的 fork 发布。
  • git ls-remote 通 ≠ 能 push:读权限随时可通,写权限被拒要到 push 才暴露。不要用 ls-remote 成功推断可写。
  • 同时确认远端协议:git remote -v。HTTPS 到 GitHub 可能整体不通(见「网络与链路故障处置」), 必要时改用 SSH:git remote set-url origin git@github.com:<owner>/<repo>.git。

阶段 1:发布线 A — DSH 插件(有上游写权限)

export HOME="/Users/johnq"
export PATH="/Users/johnq/.local/node-v22.22.3-darwin-arm64/bin:/opt/homebrew/bin:$PATH"
cd <repo>/<pkg>
npm run check && npm run test:gate && npm run pack
shasum -a 256 dist/v<ver>/*.tgz          # 两个文件必须同 sha
tar -xzf dist/v<ver>/dsh-worktable.tgz -C "$(mktemp -d)" && grep -rn "<旧仓库名>" <解包目录>   # 期望无输出
git add -A && git commit -m "release: <name> v<ver>(REQ-…)…"
git tag -f v<ver> && git push origin HEAD v<ver>
gh release create v<ver> dist/v<ver>/dsh-worktable.tgz dist/v<ver>/dsh-worktable-<ver>.tgz \
  --draft --title "<name> v<ver>" --notes-file <notes.md>
  • tag 必须等于包内版本(v + package.json#version),否则下游按 tag 拼版本化资产名会 404。
  • Release 附双资产:固定名(供 releases/latest/download/<fixed>.tgz 长期入口)+ 版本化名(可追责)。

阶段 2:发布线 B — 无上游写权限 → fork 发布

gh repo fork <upstream-owner>/<repo> --clone=false        # 已存在则复用
git remote add fork git@github.com:<me>/<repo>.git        # 已存在则 git remote set-url fork …
git fetch --no-tags origin main

2.1 干净单提交 + 树一致性 gate(必做,不可省)

OURS=$(git rev-parse <本地完成发布内容的 HEAD>)
git checkout -B release/v<ver> origin/main
git rm -rq --cached .
git checkout "$OURS" -- .
git add -A
git commit -m "release: <name> v<ver>(REQ-…)…"
git diff --stat "$OURS"        # 必须是空输出
  • git diff --stat "$OURS" 非空即 abort:切回原分支、删掉该发布分支、报告差异,不要「差一点也推」。
  • 该 gate 的真实作用是:让公开发布分支的树等于本地验收树,同时不携带本地历史提交(个人路径、调试、凭据)。
  • git rm -rq --cached . 只清索引,随后 git checkout "$OURS" -- . 覆盖工作树;若上游 main 存在本地树里已删除的 文件,残留文件会被 git add -A 重新加回导致 gate 失败——先确认 git diff --name-status origin/main <OURS> 无 D/R。
  • 提交前可顺手确认没有敏感路径混入:git log -S'<本机绝对路径前缀>' --oneline。

2.2 推分支 + tag + 附双资产的 draft Release

git push -u fork release/v<ver>
git tag -f v<ver> && git push -f fork v<ver>     # -f 只作用于 fork 的 tag
gh release create v<ver> dist/v<ver>/dsh-worktable.tgz dist/v<ver>/dsh-worktable-<ver>.tgz \
  --repo <me>/<repo> --draft --title "<name> v<ver>" --notes-file <notes.md>
  • 推完复核「原仓库零改动」:git ls-remote <origin-url>(应无新分支/tag)+ gh release list --repo <upstream>(列表应与之前一致)。
  • Release notes 至少写:新增 / 修复 / 清理 / 验证(实际跑过的测试与数字)/ 安装命令(指向 fork)/ 包 SHA-256 / 已知边界。

阶段 3:改指向(换仓库、换发布方时)

只要「发布方」变了,插件里所有指向旧仓库的常量都要一次改全,否则更新检查读的还是旧仓库 → 永远发现不了新版:

  • 更新检查仓库常量(如 UPDATE_REPO,可能有多份:入口 + 独立模块 + 设置面板各自实现);
  • 升级命令/提示词里的安装 URL(可能拼在字符串里);
  • package.json#repository.url;
  • 发布后核对脚本里硬编码的 REPO;
  • README/AGENTS/安装说明里的一行命令、git clone 地址、文档链接;
  • 任何 grep -rn "<旧 owner>" 的残留。

规则:

  • 只改指向,不改版本号与逻辑;逐处 git diff 复核。
  • 「相关项目/其它仓库」的外链如果指向另一位开发者的独立仓库,不要顺手改指(目标仓库不存在会变成死链)——保留并在交付里说明。
  • 改完必须重新 pack(铁律 3),解包 grep 确认产物内无旧仓库名,sha 与 Release 全部按新值重来。

阶段 4:draft → publish

gh release edit v<ver> --repo <me>/<repo> --draft=false
  • draft 期间 releases/latest 仍指向上一个已发布版本 → 插件内「检查更新」看不到新版本; draft 资产的 tag 路径也不公开可下(需鉴权)。这是「更新检查不生效」最常见的非 bug 原因,先查 Release 是否还是 draft。
  • 发布前用用户过目确认;发布后再跑阶段 5。

阶段 5:发布后核对

优先用仓库自带的核对脚本,但平台适配要先判定:verify-remote.mjs 之类脚本若用 powershell Invoke-WebRequest 下载,则只在 Windows 可用,在 macOS 会以 FAIL: download fixed-name asset 收场 (它的 GET URL 仍能证明改指生效,不代表脚本思路错)。

macOS/无 PowerShell 时的等价核对(全部要过):

gh release view v<ver> --repo <me>/<repo> --json assets        # 两个资产的 digest 必须都等于本地 sha
gh release download v<ver> --repo <me>/<repo> --dir "$TMP"     # draft 也可下载(已鉴权)
shasum -a 256 "$TMP"/*.tgz                                     # 与本地验收 sha 逐字一致
cmp "$TMP/dsh-worktable.tgz" dist/v<ver>/dsh-worktable.tgz     # byte-identical
tar -xzf "$TMP/dsh-worktable.tgz" -C "$TMP/ext" && grep -rn "<旧仓库名>" "$TMP/ext"   # 无输出
curl -sL -o /dev/null -w '%{http_code}\n' <版本化资产 URL>      # 发布后 200/302;draft 阶段 404 属预期

结论口径:文件名 + 结构(精确文件清单)+ 包内版本 + 双资产同源 + 远端=本地验收 SHA + 真实安装导入 全过才算发布验收。 draft 阶段只能做「远端资产 = 本地验收包」的比对,匿名可下载性要等 publish 后再验。

阶段 6:Slark 宿主补丁(宿主侧特有)

  • 改 app.asar 无效:Electron 用 ElectronAsarIntegrity 记录 asar 头哈希并有 fuse 完整性校验,直接热改会让 哈希失配,破坏签名,应用拒绝启动或行为异常。必须重打包 + 重新安装,不要在安装目录里就地改文件。
  • 宿主补丁落在 fork/分支上时:rebase 到发布基线后必须重跑全量测试(类型检查 + 全量单测), 例如 pnpm -r typecheck 与 node --import tsx --test "src/**/*.test.ts";rebase 不产生冲突不代表语义没变。
  • 宿主发布与插件发布解耦:宿主要用新插件版本,通常靠宿主内嵌 runtime 的升级,而不是「插件先发、宿主自动跟进」。

网络与链路故障处置

现象 判定 处置
curl 访问 GitHub 返回 HTTP 000(HTTPS 整体不通) 出口到 github.com:443 被掐断,与凭据无关 改走 SSH:git remote set-url origin git@github.com:<owner>/<repo>.git,并确认 SSH 身份
git fetch 长时间无输出/0 字节、卡十几分钟 链路假死,不是"慢" 判死并清理后重试(见下方循环模板),不要原地等
30 秒内报 fatal: early EOF 链路被中途掐断(代理/MTU/网络策略) 记录原文与时间点;换协议/换网络重试;不要反复 force
macOS 没有 timeout 命令 BSD 无 GNU timeout 用后台 + 轮询模板
git push 报无权限但 ls-remote 正常 只读凭据 直接走 fork 发布(阶段 2),不要重试上游

macOS 无 timeout 时的判死模板:

( git fetch --no-tags origin main ) & PID=$!
for i in $(seq 1 30); do sleep 1; kill -0 "$PID" 2>/dev/null || break; done
if kill -0 "$PID" 2>/dev/null; then kill -9 "$PID"; pkill -9 -f git-upload-pack; echo "TIMEOUT(30s) — 链路假死"; fi
  • 认证/网络失败时不做破坏性重试(尤其不要为了"绕过"而 force 到上游);把失败原文、已完成的步骤、当前指针状态写清楚上报。
  • 推送成功但平台上看不到:查 git config --get-regexp '^url\.' 是否有 pushInsteadOf 静默改写了目标。

ai-proj 对齐

  • 需求阶段:动手前立项(create_requirement,写清需求描述 / 预期结果 / 验收标准三段式 + AC),commit/PR 引用 REQ-…。 纯文档/工具/只读排查可例外。
  • 交付阶段:真实发布之后才推进 staging → released;只有计划、没有发布产物或验收证据时不要推进。
  • 归档阶段:/req done 归档前要逐条 AC 证据(命令、输出、资产 sha、URL),不能只写"已完成"。
  • 文档接口故障绕过:若 append-document-content / create-and-attach 持续报 document_versions_document_id_version_number_key 唯一约束错误(同一 (document_id, version_number) 冲突), 改用 PUT /api/v1/requirements/{id} 把说明写进需求描述——requirements 表不经过 document_versions,可绕开该约束; 不要伪造版本号或删除已有文档版本。

故障分流

现象 处置
插件「检查更新」永远看不到新版 依次查:Release 是否还是 draft;releases/latest 指向;插件内 UPDATE_REPO 是否仍指旧仓库;latest 资产名是否与代码拼的一致
pack 报结构断言失败 包内混入了 src/、.map 或多余文件;检查 files 字段与构建产物位置,不要手改 tgz
两个 tgz 的 sha 不一致 打包脚本的"同源复制"被绕过(手工 pack/改名);重新走唯一入口
git diff --stat <OURS> 非空 上游 main 有本地已删除/重命名文件残留 → 先 git rm --cached 清理并确认无 D/R,gate 不过不上推
发布分支含个人路径/凭据 commit 不要 push 该历史;改用干净单提交分支(阶段 2.1)
gh release create 报 tag 不存在/被占用 tag 必须已存在或由 create 指定;draft 会以 untagged-… 预览 URL 呈现,属正常,publish 后即 .../tags/v<ver>
draft 资产匿名下载 404 预期行为(draft 需鉴权);发布后用 curl -sL -o /dev/null -w '%{http_code}' 复验
宿主改完不生效 是否只改了 app.asar(被完整性校验挡住);必须重打包重装;再查是否加载了另一份 runtime

参考

  • references/fork-release-and-post-checks.md —— fork 发布可复制命令序列、Release notes 模板、发布后核对矩阵。
  • 同市场:slark-cicd(Slark staging/生产/Desktop)、dev-cicd(流水线设计)、ai-proj-cicd-release(AI-Proj 发布链)。
  • 权威流程以目标仓库自带的发布脚本与文档为准;本技能是清单,不替代仓库脚本的断言。