docs(release): 记录mini部署证据
Some checks failed
Backend Maven CI / backend-local (push) Has been cancelled

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lili 2026-06-27 11:22:53 -07:00
parent e4e758f787
commit fd0f80b226
5 changed files with 180 additions and 4 deletions

View File

@ -35,7 +35,18 @@ Muse 1.0.0 线 A 已具备最小可用闭环作品写作、AI 真生成、候
- Studio`tsc -b --force` 通过ESLint 0 errors / 1 既有 warningVitest 27 files / 119 tests passedPlaywright MSW-off 全量 61 tests = 60 passed / 1 skipped。
- Admin`@vben/web-antd typecheck` 通过Vitest 50 files / 347 tests passed。
## 4. 已知边界
## 4. mini-desktop 部署
- 部署时间2026-06-27 11:20-11:28 PDT。
- 入口:`mini-desktop` 实际 SSH 入口为 `root@100.64.0.7`hostname `minione-ubuntu-desktop`)。
- RC 目录:`/opt/muse-rc1`
- 端口:后端 `48081`Studio `4175`Admin `4176`;保留既有 `48080/4173/4174` 上其它项目进程。
- 运行依赖:已安装 OpenJDK 21Redis 使用 mini 本机 `127.0.0.1:6379` 受控实例,密码来自 `/root/.config/muse-repo/infra.env`,只监听 loopbackPostgreSQL 仍连 `100.64.0.8:5433/muse_slice_live`
- 前端反代:`scripts/deploy/mini-desktop/static-proxy.mjs` 负责静态文件与 `/app-api``/admin-api` 反代到 `127.0.0.1:48081`
- Smoke后端 `GET /app-api/muse/agents``/works``/knowledge-bases` 均返回 `code=0``http://100.64.0.7:4175/``http://100.64.0.7:4176/` 均 HTTP 200经 Studio 端口反代 `/app-api/muse/works` 返回 `code=0`;经 Admin 端口反代 `/admin-api/muse/ai/agents` 返回 `code=0`
- 部署修正:远端默认缺 Java 21 与 Redis`apt update` 受既有 Docker 源 GPG key 缺失影响,但直接安装 Ubuntu `openjdk-21-jre-headless``redis-server` 成功。`/actuator/health` 在当前应用被业务 404 包装成 HTTP 200不作为健康证据。
## 5. 已知边界
- GraphRAG attribution live IT 未配置独立 opt-in当前 honest skipped不计为线 A passed。
- `market-install-kb-retrieval.spec.ts` D0-fork UI 物化用例仍 `test.fixme`,后端 D0-fork/KB handoff/召回已有真 PG 与 e2e 切片证据。
@ -45,7 +56,7 @@ Muse 1.0.0 线 A 已具备最小可用闭环作品写作、AI 真生成、候
- Meta impact preview 的生产 owner usage contributor 仍要求 all-real本轮 Meta lifecycle IT 的 `VERIFIED_ZERO` 只证明状态机和装配门。
- 当前 Node v26.3.0 超出 Admin package engines 推荐范围,但本轮 typecheck/Vitest 已通过;发布流水建议使用 package engines 声明的 Node 版本。
## 5. 复跑命令
## 6. 复跑命令
```bash
cd muse-cloud
@ -70,7 +81,7 @@ pnpm -F @vben/web-antd run typecheck
pnpm test:unit
```
## 6. 回滚策略
## 7. 回滚策略
- 后端按 Epic/模块回滚,优先回滚最近变更;涉及 Flyway 的已应用迁移不做 destructive rollback采用前向修复。
- 前端回滚按 Studio/Admin 独立包回滚Playwright global setup 夹具变更若导致环境污染,先禁用对应 e2e 并复位 fixture再决定代码回退。

View File

@ -39,7 +39,7 @@
**E8 发布候选冻结与总验(2026-06-27)**:E0-E7 完成后进入 RC 总验。后端三层 fresh 复跑已过:`local` 61/0F/0E/0S`real-pg` 使用真实 PostgreSQL `_test``muse_p1r_100_release_test`47 份 fresh surefire 报告、194/0F/0E/0S`external-live` 真实调用 New-API/RAGFlowserver live 8/0F/0E/0S + module live 4/0F/0E/1S`BUILD SUCCESS`。唯一 skip 为 GraphRAG attribution 独立 opt-in 未配置,不计为线 A passedNew-API completion、AI runtime usage/quota/attribution、RAGFlow upload/parse/retrieval、Knowledge runtime、Market KB D0-fork 与 after-commit 均真跑通过。本轮 real-PG 先暴露并修正 1F/2Emarket 召回断言从旧 fail-closed 语义对齐为当前来源状态事件发布语义,两个 content IT 补齐测试上下文 bean另确认手工直跑 server IT 时必须让 surefire forked JVM 收到 `p1r.flyway.*` 和代理清理 `argLine`,改 module 后必须 `-am` 或 install 防 stale jar 假红。CI/package 兜底已补跑:首次 `mvn -B package --file pom.xml` 暴露 `MuseApiContractSupport` 缺 8 个 OpenAPI 操作登记233/241补齐后定向契约门 `MuseApiContractSupportTest` 4/0F/0E/0S根 package 61 个 reactor 模块全 SUCCESS、`BUILD SUCCESS`。前端总验已过Studio `tsc -b --force` 通过、ESLint 0 errors/1 既有 MSW warning、Vitest 27 files/119 tests passed、MSW-off Playwright 全量 61 tests=60 passed/1 skipped/0 failedAdmin `@vben/web-antd typecheck` 通过、Vitest 50 files/347 tests passed。首次 Studio 全量红暴露额度夹具缺失、work1 槽位被 agent-slot-bind 污染、知识库定位器过宽,已分别用 global-setup 额度复位/槽位隔离/卡片级定位修复React key 重复 warning 已用 `source/installId/kbId` 复合 key 消除。RC 发布说明与已知边界已落 [1.0.0-RC发布说明](1.0.0-RC发布说明.md)T8.4 完成。边界GraphRAG attribution skip、Studio D0-fork UI 物化 `test.fixme`、Admin Playwright 未跑、pay/report/bpm/mp 与线 B 后置项均不计为 1.0.0 passed。
**RC 基线记录(2026-06-27 11:05 PDT)**:`v1.0.0-rc.1` 指向 `3585219 feat(mvp): 收束1.0.0线A交付闭环`,分支 `dev/1.0.0` 已推送到 `origin/dev/1.0.0`部署到 `mini-desktop` 需另行 smoke 证明,不能仅凭 tag 视为已部署
**RC 基线记录(2026-06-27 11:05 PDT)**:`v1.0.0-rc.1` 指向 `3585219 feat(mvp): 收束1.0.0线A交付闭环`,分支 `dev/1.0.0` 已推送到 `origin/dev/1.0.0`**mini-desktop 部署已完成(2026-06-27 11:28 PDT)**:实际入口 `root@100.64.0.7`,部署目录 `/opt/muse-rc1`,端口后端 `48081` / Studio `4175` / Admin `4176`,保留既有 `48080/4173/4174` 其它项目进程。远端补齐 OpenJDK 21 与本机 loopback Redis后端业务 smoke `GET /app-api/muse/agents|works|knowledge-bases``code=0`Studio/Admin HTML 均 HTTP 200`4175/app-api/muse/works``4176/admin-api/muse/ai/agents` 反代均 `code=0`。部署细节与边界见 [1.0.0-RC发布说明 §4](1.0.0-RC发布说明.md#4-mini-desktop-部署)
**market 写路径整改(2026-06-14,ultracode)**:member 暴露写端口 `MuseAccountRecordProjectionApi` + DTO(member-server 实现读写自有 DAL、tenantId 由实现侧从上下文注入防伪造、事务沿用调用方),market 5 类改消费端口、移除 member.dal 依赖 → `KNOWN_VIOLATION_EXEMPTIONS` 清空、BC 门全绿。**附带修复**:round-2 重构 `ContentKnowledgeWorkOwnerFacade` 时遗留的旧测试 `KnowledgeWorkOwnerFacadeTest`(仍断言旧 WorkMapper 行为)已删除,其装配守卫/兜底两用例并入 `ContentKnowledgeWorkOwnerFacadeTest`(5/0F)——此为 round-2 一处假绿(当时构建在平台时区用例处中止、未真正跑到 knowledge),现已补正。

View File

@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -euo pipefail
# mini-desktop 只承载 Muse RC 进程,基础设施继续复用 mini-ubuntu-infra。
# 凭据文件只在运行时 source脚本本身绝不保存密码、Token 或 API Key。
ROOT="${MUSE_RC_ROOT:-/opt/muse-rc1}"
ENV_FILE="${MUSE_INFRA_ENV:-/root/.config/muse-repo/infra.env}"
AI_ENV_FILE="${MUSE_AI_ENV:-/root/.config/muse-repo/p1r-external-acceptance.env}"
if [[ -f "${ENV_FILE}" ]]; then
set -a
# shellcheck disable=SC1090
source "${ENV_FILE}"
set +a
fi
if [[ -f "${AI_ENV_FILE}" ]]; then
set -a
# shellcheck disable=SC1090
source "${AI_ENV_FILE}"
set +a
fi
# Redis 由本部署目录的 start-redis.sh 在 mini-desktop 本机拉起;这里保留 env 默认值。
export MUSE_POSTGRES_HOST="${MUSE_POSTGRES_HOST:-100.64.0.8}"
export MUSE_POSTGRES_PORT="${MUSE_POSTGRES_PORT:-5433}"
export MUSE_POSTGRES_DATABASE="${MUSE_POSTGRES_DATABASE:-muse_slice_live}"
export MUSE_POSTGRES_USERNAME="${MUSE_POSTGRES_USERNAME:-root}"
export MUSE_REDIS_HOST="${MUSE_REDIS_HOST:-127.0.0.1}"
export MUSE_REDIS_PORT="${MUSE_REDIS_PORT:-6379}"
exec /usr/lib/jvm/java-21-openjdk-amd64/bin/java \
-Xms512m -Xmx2g \
-Dhttp.proxyHost= \
-Dhttps.proxyHost= \
-Djava.net.useSystemProxies=false \
-DsocksProxyHost= \
-DsocksProxyPort= \
-jar "${ROOT}/backend/muse-server.jar" \
--server.port=48081 \
--spring.profiles.active=local,infra \
--muse.codegen.import-enable=false

View File

@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail
# RC 部署仅需要一个本机 Redis 缓存/会话实例;密码从私有 env 读取,配置文件仅 root 可读。
ROOT="${MUSE_RC_ROOT:-/opt/muse-rc1}"
ENV_FILE="${MUSE_INFRA_ENV:-/root/.config/muse-repo/infra.env}"
REDIS_PORT="${MUSE_REDIS_PORT:-6379}"
if [[ -f "${ENV_FILE}" ]]; then
set -a
# shellcheck disable=SC1090
source "${ENV_FILE}"
set +a
fi
if [[ -z "${MUSE_REDIS_PASSWORD:-}" ]]; then
echo "缺少 MUSE_REDIS_PASSWORD拒绝启动 Redis。" >&2
exit 1
fi
install -d -m 700 "${ROOT}/redis" "${ROOT}/logs" "${ROOT}/run"
umask 077
cat > "${ROOT}/redis/redis.conf" <<EOF
bind 127.0.0.1
port ${REDIS_PORT}
protected-mode yes
requirepass ${MUSE_REDIS_PASSWORD}
dir ${ROOT}/redis
dbfilename dump.rdb
appendonly no
save ""
logfile ${ROOT}/logs/redis.log
EOF
exec redis-server "${ROOT}/redis/redis.conf" --daemonize no

View File

@ -0,0 +1,88 @@
import http from 'node:http';
import { createReadStream, existsSync, statSync } from 'node:fs';
import { extname, join, normalize, resolve } from 'node:path';
// 这是 mini-desktop RC 部署的轻量静态服务:同时把前端相对 API 路径反代到 muse-server。
// 生产化部署应换成 Nginx/CaddyRC smoke 用 Node 单文件可减少额外系统依赖和端口冲突。
const args = Object.fromEntries(
process.argv.slice(2).map((arg) => {
const [key, ...rest] = arg.replace(/^--/, '').split('=');
return [key, rest.join('=') || ''];
}),
);
const root = resolve(args.root || '.');
const port = Number(args.port || 4175);
const backend = new URL(args.backend || 'http://127.0.0.1:48081');
const proxyPrefixes = (args.proxy || '/app-api,/admin-api,/sse').split(',').filter(Boolean);
const contentTypes = new Map([
['.html', 'text/html; charset=utf-8'],
['.js', 'text/javascript; charset=utf-8'],
['.mjs', 'text/javascript; charset=utf-8'],
['.css', 'text/css; charset=utf-8'],
['.json', 'application/json; charset=utf-8'],
['.png', 'image/png'],
['.jpg', 'image/jpeg'],
['.jpeg', 'image/jpeg'],
['.svg', 'image/svg+xml'],
['.ico', 'image/x-icon'],
['.woff', 'font/woff'],
['.woff2', 'font/woff2'],
['.ttf', 'font/ttf'],
['.eot', 'application/vnd.ms-fontobject'],
]);
function sendNotFound(res) {
res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
res.end('Not Found');
}
function proxy(req, res) {
const target = new URL(req.url || '/', backend);
const headers = { ...req.headers, host: backend.host };
const upstream = http.request(target, { method: req.method, headers }, (proxyRes) => {
res.writeHead(proxyRes.statusCode || 502, proxyRes.headers);
proxyRes.pipe(res);
});
upstream.on('error', (error) => {
res.writeHead(502, { 'content-type': 'application/json; charset=utf-8' });
res.end(JSON.stringify({ code: 502, msg: `backend unavailable: ${error.code || error.message}` }));
});
req.pipe(upstream);
}
function serve(req, res) {
const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
if (proxyPrefixes.some((prefix) => url.pathname === prefix || url.pathname.startsWith(`${prefix}/`))) {
proxy(req, res);
return;
}
const decodedPath = decodeURIComponent(url.pathname);
const relative = normalize(decodedPath).replace(/^\/+/, '');
let file = resolve(join(root, relative));
if (!file.startsWith(root)) {
sendNotFound(res);
return;
}
// SPA 深链统一回落到 index.html避免刷新路由时 404。
if (!existsSync(file) || statSync(file).isDirectory()) {
file = join(root, 'index.html');
}
if (!existsSync(file) || statSync(file).isDirectory()) {
sendNotFound(res);
return;
}
const type = contentTypes.get(extname(file).toLowerCase()) || 'application/octet-stream';
res.writeHead(200, { 'content-type': type });
createReadStream(file).pipe(res);
}
http.createServer(serve).listen(port, '0.0.0.0', () => {
console.log(`static proxy listening on ${port}, root=${root}, backend=${backend.origin}`);
});