---
name: dsh-web-ui-release
description: Release and publish the dsh-web-ui monorepo (DSH Web GUI plugin family + skin collection) — bump all packages to one unified version, commit and tag, push the vX.Y.Z tag that triggers the GitHub Actions publish pipeline, and verify the npm publish + GitHub Release. Covers post-release verification and bad-version recovery. Use when the user asks to 发布/发版/release/bump 版本/publish a new version of dsh-web-ui or any @linxin666/dsh-* package.
whenToUse: The user wants to release dsh-web-ui (发布新版、发个版本、release、tag、publish @linxin666/dsh-* 包), build or change the release pipeline (release 管线、CI 发布), or recover from a bad published version (坏包、回滚、deprecate). Not for routine commits, skin development (see skin-developer skill), or CI-only changes without a release.
---

# dsh-web-ui 发布（release / publish）

本技能固化 dsh-web-ui 全家桶的完整发版流程：全仓统一版本 → 提交 → 打 tag → 推送触发
GitHub Actions 发布管线（构建/测试/npm 发布/GitHub Release）→ 发布后验证。

## 仓库事实（先读，决定每一步怎么做）

- 仓库：zhu1090093659/dsh-web-ui（**PUBLIC**），本机路径 /Users/zcl/code/dsh-web-ui。
- 全家桶 23 个包：packages/dsh-*（12 个）+ packages/skins/*（11 个，含 skin-center）。
  全部发布到 npm scope `@linxin666`，registry 固定 registry.npmjs.org。
- **版本策略：全仓统一版本**（tag vX.Y.Z = 每个 package.json 的 version，由管线强制校验）。
- npm 不允许重复发布同一版本号：已发布过的版本号（如 0.1.3/0.1.4/0.1.5）不可重发，
  只能 bump 到下一个版本。
- 发布通道：本机通常没有 npm 登录态（`npm whoami` 401 属正常）；npm 发布全部由
  GitHub Actions 管线完成，使用仓库 secret `NPM_TOKEN`（npm automation token，@linxin666 scope）。
- 根 package.json 是 private（不发布）；`pnpm -r publish` 自动跳过。
- 仓库禁 emoji（所有文件含提交信息与 tag 信息）；CI 会校验。

## 0. 发版前检查（本地全绿才允许打 tag）

```sh
cd /Users/zcl/code/dsh-web-ui
git status --short                 # 明确本次要提交的内容，无意外文件
pnpm test                          # 全仓测试
pnpm test:scripts                  # 脚本测试（link-profile 等）
node scripts/aggregate.mjs --check # 聚合清单与磁盘一致（改过 aggregate.yml 时必须先重跑生成）
git log --oneline -5               # 确认包含本次全部改动、无未推送提交
```

皮肤相关变更（skin.json / 皮肤 bundle）额外跑：

```sh
node packages/dsh-skins/build.mjs  # 重生成 dsh-skins/skins/ 载体，git status 确认无意外增删
```

**版本 bump 后必须重建产物并同步 gallery 资产**（版本信息影响 bundle 内容）：

```sh
pnpm build                 # 全仓重建 lib 产物（含新版本号）
node scripts/gallery-build # 重新生成 gallery/（manifest.js/bundles.js 内嵌产物内容）
pnpm gallery:check         # 必须通过；产物与 gallery 资产要同一次构建一起提交
```

## 1. 版本 bump（全仓统一）

```sh
find packages -name package.json -not -path '*/node_modules/*' \
  -exec sed -i '' 's/"version": "[0-9][^"]*"/"version": "X.Y.Z"/' {} +
find packages -name package.json -not -path '*/node_modules/*' \
  -exec grep -H '"version"' {} \; | grep -v '"version": "X.Y.Z"'   # 必须无输出
```

pnpm-lock.yaml 不记录包版本，无需改动；聚合包依赖用 workspace:*，发布时由 pnpm 自动替换为
实际版本，无需手工改依赖链。

## 2. 提交与 tag

提交按两类拆分（保持历史可读）：

```sh
# 修复/功能改动（含构建产物 lib/*.js 与聚合重生成的 cordis.patch.yml）
git add <修复文件...>
git commit -m "fix(...): <改动摘要>"

# 发版提交：全部 23 个 package.json 版本 bump + 发布相关变更（管线、skill、AGENTS.md）
git add packages/**/package.json .github/workflows/release.yml .dsh/skills/ AGENTS.md
git commit -m "chore(release): bump to X.Y.Z"

git tag "vX.Y.Z"                    # tag 命名固定 v 前缀；tag 即版本事实源
git push origin main
git push origin "vX.Y.Z"            # 推送 tag 即触发发布管线（唯一发布开关）
```

## 3. 发布管线（tag 触发，.github/workflows/release.yml）

推送 v* tag 后 GitHub Actions 自动执行，顺序：

1. actionlint + pnpm install（frozen lockfile，checkout 用 fetch-depth: 0 取全量历史）；
2. 全量 gate：typecheck / build / test / test:scripts / aggregate --check；
3. **版本一致性校验**：tag 版本必须与全部 23 个包的 package.json version 完全一致，不一致直接失败（防止忘 bump 就发版）；
4. **生成 release notes**：`node scripts/release-notes.mjs $TAG` 把上一 tag 以来的**全部**常规提交（含合并进来的分支提交，不能只走 --first-parent——v0.1.15 曾因此漏掉整条 perf/refactor 分支）分组为 新功能/修复/其他 并链接 issue，写在 notes 文件（发布前执行，失败即中止，不触碰 npm）；
5. `pnpm -r publish --no-git-checks --access public`（NPM_TOKEN 写入 ~/.npmrc，拓扑序发布，workspace:* 自动转真实版本）；
6. `gh release create --notes-file` 创建 GitHub Release（notes 即第 4 步生成的内容）；
7. **上传 npm tarball 资产**：`node scripts/release-assets.mjs $TAG <outDir>` 从 registry 逐包 `npm pack <name>@<version>`（与已发布内容字节一致），再 `gh release upload` 附到 Release——裸 `gh release create` 只有 GitHub 自动源码归档，不带 npm 包。

关注与排障：

```sh
gh run watch                          # 跟踪最新 run
gh run list --workflow=release.yml    # 查历史
```

- 版本不一致失败 → 本地把漏掉的包 bump 到 tag 版本，amend/新提交后**删除远端 tag 重新推送**（npm 发布前失败无副作用）。
- `NPM_TOKEN` secret 缺失/过期 → 到仓库 Settings → Secrets and variables → Actions 更新后再重跑。
- 发布中途部分包已上 npm、部分失败（网络中断等）→ **不要重推同一 tag**：已发布的版本号不可重发；
  对已发且完好的包跳过重发（pnpm publish 对已存在版本会报错，可逐个对剩余包执行发布），
  或整体 bump 到下一个补丁版本重新发布。
- 发布的是坏包（内容错误但版本已占用）→ 用 `npm deprecate` 标记弃用并立即发下一个补丁版本，不尝试覆盖。

## 4. 发布后验证（必须逐项执行）

```sh
npm view @linxin666/dsh-web-ui-all version          # 期望 = X.Y.Z
npm view @linxin666/dsh-client-ui-skin-center version
gh release view "vX.Y.Z"                            # Release 已创建、notes 为分类更新说明（scripts/release-notes.mjs 生成）
gh release view "vX.Y.Z" --json assets               # 23 个 @linxin666/dsh-* tgz 资产已附上（scripts/release-assets.mjs 上传）
gh run list --workflow=release.yml                  # 全部成功
git ls-remote --tags origin | grep "vX.Y.Z"         # tag 已在远端
```

## 5. 纪律

- tag 一旦推送且 npm 发布成功，同一版本号永不复用；补救只走「下一补丁版本」或 deprecate。
- 发版前必须本地全量测试通过；管线里的版本一致性校验是最后防线，不是唯一防线。
- 变更皮肤后先跑 build.mjs、变更聚合清单后先重跑 aggregate.mjs，再走本流程。
- **构建产物内嵌绝对路径**（CSS-module 类名哈希与 \0dsh-css region 标记），同一源码在不同
  checkout 路径下构建字节不同。因此 CI 的 gallery/skin-center 一致性检查是「提交完整性」语义
  （--ignore-scripts 安装 + 检查放在 Build 之前）：提交者必须把「产物 + gallery 资产」同一次
  构建一起提交；不要试图在 CI 里重新构建后做一致性比对。
- 提交信息、tag、Release 标题均禁 emoji（仓库硬性规则，CI 强制）。
- 本技能适用于 @linxin666/dsh-* 全家桶整体发版；单包 hotfix 也遵循同一流程（版本仍全仓统一）。
