docs(release): 记录mini部署证据
Some checks failed
Backend Maven CI / backend-local (push) Has been cancelled
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:
parent
e4e758f787
commit
fd0f80b226
@ -35,7 +35,18 @@ Muse 1.0.0 线 A 已具备最小可用闭环:作品写作、AI 真生成、候
|
||||
- Studio:`tsc -b --force` 通过;ESLint 0 errors / 1 既有 warning;Vitest 27 files / 119 tests passed;Playwright 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 21;Redis 使用 mini 本机 `127.0.0.1:6379` 受控实例,密码来自 `/root/.config/muse-repo/infra.env`,只监听 loopback;PostgreSQL 仍连 `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,再决定代码回退。
|
||||
|
||||
@ -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/RAGFlow,server live 8/0F/0E/0S + module live 4/0F/0E/1S,`BUILD SUCCESS`。唯一 skip 为 GraphRAG attribution 独立 opt-in 未配置,不计为线 A passed;New-API completion、AI runtime usage/quota/attribution、RAGFlow upload/parse/retrieval、Knowledge runtime、Market KB D0-fork 与 after-commit 均真跑通过。本轮 real-PG 先暴露并修正 1F/2E:market 召回断言从旧 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 failed;Admin `@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),现已补正。
|
||||
|
||||
|
||||
42
scripts/deploy/mini-desktop/start-backend.sh
Normal file
42
scripts/deploy/mini-desktop/start-backend.sh
Normal 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
|
||||
35
scripts/deploy/mini-desktop/start-redis.sh
Normal file
35
scripts/deploy/mini-desktop/start-redis.sh
Normal 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
|
||||
88
scripts/deploy/mini-desktop/static-proxy.mjs
Normal file
88
scripts/deploy/mini-desktop/static-proxy.mjs
Normal 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/Caddy;RC 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}`);
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user