Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/GITHUB_MAINTENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ head 分支,便于本地回看与后续跟进。请不要开启该设置。

未开启自动合并(auto-merge)。

已合并分支的清理只走 dry-run 清单,确认后再删:

```bash
npm run branches:merged # 只列已合并进 origin/main 的候选
npm run branches:merged -- --apply --yes # 明确确认后才删除
```

脚本保护 `main` / `release/*` / `release-prep/*` / `site*`、当前分支、worktree 分支和
open PR head;细节见 [`docs/operations/RELEASE.md`](../docs/operations/RELEASE.md#81-已合并远端分支清理)。

## Dependabot

- minor/patch 分组自动提 PR;`electron` 与 `electron-builder` 的 **major 一律人工处理**(`version-update:semver-major` ignore),避免打包/签名链路被自动跨大版本。
105 changes: 44 additions & 61 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ name: Publish
# → desktop-macos / desktop-windows / desktop-linux
# (同一批 tarball → runtime → 安装包 → packaged runtime smoke)
# → publish(直接发布 verify 产出的 5 个 lockstep tarball)
# → publish-dsh(独立版本插件 tarball)
# → publish-dsh(dsh-plugin tarball)
# → visibility(六包均可 npm view + latest 指向本次版本才继续)
# → release(三平台安装包 + SHA256SUMS.txt,幂等上传)
#
# workflow_dispatch 是 packaging dry-run:只跑 verify + desktop matrix 并上传 artifacts,
# publish / publish-dsh / release 全部跳过;TAG 从根 package.json 版本生成。
# publish / publish-dsh / visibility / release 全部跳过;TAG 从根 package.json 版本生成。
run-name: Publish ${{ github.ref_name }} · ${{ github.event_name == 'push' && 'release' || 'dry-run (no publish)' }}

on:
Expand Down Expand Up @@ -86,65 +87,12 @@ jobs:
if [ "$MODE" = "dry-run" ]; then
echo "::notice title=Packaging dry-run::$TAG · 只构建三平台安装包并上传 artifacts;不发布 npm、不创建 GitHub Release。"
else
echo "::notice title=Release::$TAG · 安装包全部通过后发布 npm,最后上传 GitHub Release 资产。"
echo "::notice title=Release::$TAG · 安装包全部通过后发布 npm,六包可见后上传 GitHub Release 资产。"
fi

- name: Verify version consistency (branch / 6 manifests / desktop / CHANGELOG)
- name: Verify version SSOT (branch / 6 manifests / locks / MCP / CHANGELOG)
shell: bash
run: |
node - <<'NODE'
const fs = require('node:fs');
const version = process.env.VERSION;
const tag = process.env.TAG;
const targets = [
'.',
'packages/fleet',
'packages/mcp',
'packages/service',
'packages/skill',
'apps/desktop',
];
let ok = true;
for (const rel of targets) {
const pkg = JSON.parse(fs.readFileSync(`${rel}/package.json`, 'utf8'));
if (pkg.version !== version) {
console.error(`ERROR: ${rel}/package.json version=${pkg.version} != ${version}`);
ok = false;
}
}

// 桌面 App 独立 lockfile(不属于根 workspaces)也必须与发布版本一致。
const lock = JSON.parse(fs.readFileSync('apps/desktop/package-lock.json', 'utf8'));
if (lock.version !== version || lock.packages?.['']?.version !== version) {
console.error(
`ERROR: apps/desktop/package-lock.json version=${lock.version} / packages[""].version=` +
`${lock.packages?.['']?.version} != ${version}`,
);
ok = false;
}

// 打包态 / 诊断共用的 APP_VERSION。
const daemon = fs.readFileSync('apps/desktop/src/main/daemon.mjs', 'utf8');
const appVersion = /export const APP_VERSION = '([^']+)'/.exec(daemon)?.[1];
if (appVersion !== version) {
console.error(`ERROR: apps/desktop/src/main/daemon.mjs APP_VERSION=${String(appVersion)} != ${version}`);
ok = false;
}

const changelog = fs.readFileSync('CHANGELOG.md', 'utf8');
if (!changelog.split('\n').some((line) => line.startsWith(`## [${tag}]`))) {
console.error(`ERROR: CHANGELOG.md 缺少 [${tag}] 章节(release notes 取自该章节)`);
ok = false;
}

if (!ok) {
console.error(`修复:在 release 分支上运行 \`bash scripts/bump_version.sh ${tag}\`,或重命名分支。`);
process.exit(1);
}
console.log(
`version consistency OK: ${tag}(branch + 6×package.json + desktop lock/APP_VERSION + CHANGELOG)`,
);
NODE
run: node scripts/verify-release-version.mjs --version "$VERSION" --tag "$TAG"

- name: Install dependencies
run: npm ci
Expand Down Expand Up @@ -518,7 +466,7 @@ jobs:
publish "release-pack/mebular-mcp-${VERSION}.tgz" "@mebular/mcp"
publish "release-pack/mebular-fleet-${VERSION}.tgz" "@mebular/fleet"

# 插件独立版本:不参与 lockstep,版本只取 packages/dsh-plugin/package.json;已发布则跳过。
# 插件仍由独立 job 发布;发布版本必须与六包 SSOT 一致(verify:release-version 校验)。
publish-dsh:
name: npm publish dsh-plugin tarball (push only)
needs: publish
Expand Down Expand Up @@ -568,10 +516,45 @@ jobs:
npm publish "$(realpath "$FILE")" --access public --tag latest
fi

# 发布后 gate:六包都能 npm view,且 dist-tags.latest 指向本次版本后才创建 GitHub Release。
visibility:
name: npm visibility gate (6 packages + latest, push only)
needs: [verify, publish, publish-dsh]
if: github.event_name == 'push' && needs.publish.result == 'success' && needs.publish-dsh.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
env:
VERSION: ${{ needs.verify.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24

- name: Wait for six npm packages + latest tag
shell: bash
run: |
set -euo pipefail
DSH_VERSION="$(node -p "require('./packages/dsh-plugin/package.json').version")"
node scripts/verify-release-visibility.mjs \
--version "$VERSION" \
--dsh-version "$DSH_VERSION" \
--timeout-seconds 600 \
--interval-seconds 10

release:
name: GitHub Release installers + checksums (push only)
needs: [verify, publish, publish-dsh, desktop-macos, desktop-windows, desktop-linux]
if: github.event_name == 'push' && needs.publish.result == 'success' && needs.publish-dsh.result == 'success'
needs: [verify, publish, publish-dsh, visibility, desktop-macos, desktop-windows, desktop-linux]
if: >-
github.event_name == 'push'
&& needs.publish.result == 'success'
&& needs.publish-dsh.result == 'success'
&& needs.visibility.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
Expand Down
28 changes: 22 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
# Changelog

All notable changes to Mebular are documented here.
The five lockstep packages always share one version; `@mebular/dsh-plugin` keeps its own
version line.
Mebular uses a single-version umbrella: the six npm packages (`core`, `service`, `skill`, `mcp`,
`fleet`, `dsh-plugin`) share one release version. `@mebular/dsh-plugin` is an isolated adapter
published by its own npm job, and it follows the same unified release train.

The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

> Version headings carry the `v` prefix (`## [v0.1.1]`) because it is the release SSOT:
> the branch name (`release/v0.1.1`), the five lockstep `package.json` versions and the Git
> tag must all agree with this section. `@mebular/dsh-plugin` keeps its own version line but
> ships `0.1.1` with this release.
> the branch name (`release/vX.Y.Z`), the six `package.json` versions, the locks, the MCP
> version source and the Git tag must all agree with this section.

The GitHub Release body is extracted from this file: everything between `## [v0.1.1]` and the
next `## [` heading. Keep that section self-contained.

## [Unreleased]

Nothing yet.
### Changed

- **Release engineering** — `scripts/bump_version.sh` now bumps all six release packages,
both lockfiles, the desktop `APP_VERSION` and the single MCP version source in
`packages/mcp/src/version.mjs`. `npm run verify:release-version` checks the same SSOT
locally and in the publish workflow.
- **npm visibility gate** — after `publish` / `publish-dsh`, the workflow polls
`npm view <pkg>@<version> version` and `dist-tags.latest` for all six packages before
creating the GitHub Release. The previous v0.1.1 run could create the Release while a
package was still propagating.
- **Single-version umbrella** — all six npm packages share one release version. The DSH
adapter keeps its code isolated in `packages/dsh-plugin` and is published by its own npm
job, but it follows the unified release train; no release ships the DSH plugin alone.
- **Release SOP** — the fixed flow is now `release-prep/vX.Y.Z` → packaging dry-run →
`release/vX.Y.Z` → back-merge `main` → delete release/prep branches. A dry-run-only
branch cleanup helper is documented for merged remote branches.


## [v0.1.1] - 2026-10-25

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ mebular print-config --client opencode # MCP config
<p align="center"><strong>Use with DSH</strong><br>
Keep the Mebular app running, then install the plugin from npm into DSH's web profile:<br>
<code>dsh plugin --profile web add @mebular/dsh-plugin@0.1.1</code><br>
A Mebular sidebar panel embeds the same Console through <code>?embed=dsh</code> (loopback only), and the official MCP client reaches the same graph. The plugin discovers the daemon by <code>daemonUrl &gt; MEBULAR_HTTP_PORT &gt; runtime.json &gt; 7331</code>, so custom ports follow automatically. It has its own version line and is not part of the five-package lockstep. For plugin development, install from <code>packages/dsh-plugin</code> instead.<br>
A Mebular sidebar panel embeds the same Console through <code>?embed=dsh</code> (loopback only), and the official MCP client reaches the same graph. The plugin discovers the daemon by <code>daemonUrl &gt; MEBULAR_HTTP_PORT &gt; runtime.json &gt; 7331</code>, so custom ports follow automatically. The plugin is an independent adapter with its code isolated in <code>packages/dsh-plugin</code>; it is published by its own npm job, while its version follows the unified release train, so all six npm packages share one version. For plugin development, install from <code>packages/dsh-plugin</code> instead.<br>
Listed on <a href="https://github.com/awesome-dsh-plugin/awesome-dsh-plugin">awesome-dsh-plugin</a> under <code>memory</code>.<br>
<a href="packages/dsh-plugin/README.md">DSH plugin guide →</a> · <a href="docs/integrations/DSH.md">Integration notes →</a></p>

Expand Down
2 changes: 1 addition & 1 deletion README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ mebular print-config --client opencode # MCP 配置
<p align="center"><strong>与 DSH 一起使用</strong><br>
保持 Mebular App 运行,然后从 npm 把插件装入 DSH 的 web profile:<br>
<code>dsh plugin --profile web add @mebular/dsh-plugin@0.1.1</code><br>
侧边栏 Mebular 面板通过 <code>?embed=dsh</code>(仅 loopback)嵌入同一套 Console,官方 MCP client 连接同一张图。插件按 <code>daemonUrl &gt; MEBULAR_HTTP_PORT &gt; runtime.json &gt; 7331</code> 发现 daemon,自定义端口会自动跟随。插件有独立版本线,不进入五包 lockstep;开发时仍可从 <code>packages/dsh-plugin</code> 本地安装。<br>
侧边栏 Mebular 面板通过 <code>?embed=dsh</code>(仅 loopback)嵌入同一套 Console,官方 MCP client 连接同一张图。插件按 <code>daemonUrl &gt; MEBULAR_HTTP_PORT &gt; runtime.json &gt; 7331</code> 发现 daemon,自定义端口会自动跟随。插件是独立适配器,代码隔离在 <code>packages/dsh-plugin</code>;它由独立 npm job 发布,版本随统一 release train,六个 npm 包同版本;开发时仍可从 <code>packages/dsh-plugin</code> 本地安装。<br>
已收录于 <a href="https://github.com/awesome-dsh-plugin/awesome-dsh-plugin">awesome-dsh-plugin</a> 的 <code>memory</code> 分类。<br>
<a href="packages/dsh-plugin/README.md">DSH 插件指南 →</a> · <a href="docs/integrations/DSH_CN.md">集成说明 →</a></p>

Expand Down
13 changes: 9 additions & 4 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
The release operations for this repository live under `docs/operations/`:

- [Release SOP](docs/operations/RELEASE.md) — npm six packages, the `verify → preflight →
desktop matrix → publish → publish-dsh → release` pipeline, dry-run, checksums and rollback.
desktop matrix → publish → publish-dsh → visibility → release` pipeline, the
`release-prep → dry-run → release → back-merge` branch flow, checksums and rollback.
- [Desktop release](docs/operations/DESKTOP_RELEASE.md) — runtime packaging from release
tarballs, installer asset names, unsigned install steps, and the signing/notarization plan.
- [Changelog](CHANGELOG.md) — release notes; the `## [vX.Y.Z]` section is the source of the
GitHub Release body.

The current release line is **v0.1.1**: five lockstep npm packages plus the independently
versioned `@mebular/dsh-plugin@0.1.1`, and unsigned desktop installers for macOS, Windows
and Linux. Signing, notarization, and auto-update are not part of this release.
The current release line is **v0.1.1**: a single-version umbrella with six npm packages at
`0.1.1` (`core`, `service`, `skill`, `mcp`, `fleet`, `dsh-plugin`), and unsigned desktop
installers for macOS, Windows and Linux. The DSH plugin is an isolated adapter published by
its own npm job, but it follows the unified release train.
From the next release on, `bump_version.sh` updates all six packages and
`verify:release-version` / the publish workflow enforce the same version. Signing,
notarization, and auto-update are not part of this release.
16 changes: 8 additions & 8 deletions docs/integrations/DSH.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ For plugin development, install from the repository checkout instead:
dsh plugin --profile web add ./packages/dsh-plugin
```

`@mebular/dsh-plugin` has its own version line: it ships with the Mebular v0.1.1 release
train, but its version comes only from `packages/dsh-plugin/package.json` and it is not part
of the five-package lockstep. The plugin is listed in
`@mebular/dsh-plugin` is an independent adapter: its code stays isolated in
`packages/dsh-plugin`, and it is published by its own npm job. Its version follows the
unified Mebular release train, so all six npm packages share one version. The plugin is listed in
[awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) under **memory**
(PR [#9167](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/9167)).

Expand Down Expand Up @@ -168,11 +168,11 @@ whole, not a separate package repo, so discovery points back here. See the

## Release and versioning

- The plugin has its own version, `0.1.1`, and its own npm lifecycle.
- It ships on the **same release train** as Mebular v0.1.1; both packages are at `0.1.1`
in this release.
- It is **not** part of the five-package lockstep (`core`, `service`, `skill`, `mcp`,
`fleet`). Those five always share one version; the plugin can move independently.
- The plugin is an independent adapter: its code is isolated in `packages/dsh-plugin`, and its
npm publish runs in its own `publish-dsh` job.
- All six npm packages (`core`, `service`, `skill`, `mcp`, `fleet`, `dsh-plugin`) share one
version from the unified Mebular release train. The plugin is included in every release;
no release ships the DSH plugin alone.
- Publish is idempotent: the `publish-dsh` workflow job reads the version from
`packages/dsh-plugin/package.json`, skips it if `npm view` already sees that version, and
otherwise publishes the package. See [the release SOP](../operations/RELEASE.md).
Expand Down
12 changes: 6 additions & 6 deletions docs/integrations/DSH_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ dsh plugin --profile web add @mebular/dsh-plugin@0.1.1
dsh plugin --profile web add ./packages/dsh-plugin
```

`@mebular/dsh-plugin` 是独立版本线:随 Mebular v0.1.1 release train 发布,但版本号只取自
`packages/dsh-plugin/package.json`,不进入五包 lockstep。插件已收录于
`@mebular/dsh-plugin` 是独立适配器:代码隔离在 `packages/dsh-plugin`,由独立 npm job
发布;版本随统一 Mebular release train,六个 npm 包同版本。插件已收录于
[awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) 的 **memory**
分类(PR [#9167](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/9167))。

Expand Down Expand Up @@ -155,10 +155,10 @@ Mebular 仓库,而不是一个独立包仓库,所以搜索会回到这里。

## 发布与版本

- 插件独立版本 `0.1.1`,有独立的 npm 生命周期。
- 它和 Mebular v0.1.1 走**同一条 release train**;本版两者都是 `0.1.1`。
- 它**不进入**五包 lockstep(`core`、`service`、`skill`、`mcp`、`fleet`):那五个包永远同
版本,插件可以单独前进。
- 插件是独立适配器:代码隔离在 `packages/dsh-plugin`,npm 发布由独立的 `publish-dsh`
job 执行。
- 六个 npm 包(`core`、`service`、`skill`、`mcp`、`fleet`、`dsh-plugin`)共用统一
Mebular release train 的同一版本。每次发布都包含该插件,不单独为 DSH 插件发版。
- 发布是幂等的:`publish-dsh` job 从 `packages/dsh-plugin/package.json` 读取版本,
`npm view` 已存在则跳过,否则执行发布。详见[发布 SOP](../operations/RELEASE.md)。

Expand Down
6 changes: 4 additions & 2 deletions docs/operations/DESKTOP_RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,10 @@ AppImage 需要 FUSE;没有 FUSE 时可使用 `--appimage-extract-and-run`。d
| preflight(npm whoami) | ❌ | ✅ |
| desktop matrix(三平台) | ✅ | ✅ |
| npm publish(5 + 1) | ❌ | ✅ |
| npm visibility gate(6 包 + latest) | ❌ | ✅ |
| GitHub Release + 资产 | ❌ | ✅ |
| `SHA256SUMS.txt` | ❌(仅 artifacts) | ✅ |
| 版本来源 | 根 `package.json` | 分支名 `release/vX.Y.Z` |
| 版本来源 | prep 分支根 `package.json` | 分支名 `release/vX.Y.Z` |

dry-run 的 artifacts:

Expand All @@ -238,7 +239,8 @@ dry-run 的 artifacts:
- `desktop-linux`:AppImage + deb。

正式流水线中,`publish` / `publish-dsh` 依赖三个 desktop job 全部成功,因此**安装包没全绿
就不会发 npm**。`release` job 再下载三平台 artifacts,校验每种扩展名恰好 1 个,生成
就不会发 npm**。`visibility` job 再确认六包均可 `npm view` 且 `dist-tags.latest` 指向本次
版本;只有它通过后,`release` job 才下载三平台 artifacts,校验每种扩展名恰好 1 个,生成
`SHA256SUMS.txt`,创建/更新 GitHub Release 并 `--clobber` 上传。

本地或 CI 都可以对解包目录跑 packaged runtime smoke:
Expand Down
Loading
Loading