From c2377f96effe03a730bc75e9b0dbc29cb6cc608d Mon Sep 17 00:00:00 2001 From: sunlei Date: Thu, 18 Jun 2026 09:14:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=A2=9E=E5=8A=A0QQBot=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E7=94=9F=E4=BA=A7=E8=B7=AF=E5=BE=84=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/selfTest.ts | 54 +++++++++++++++++++++++++++++++++++ src/tools/review.ts | 69 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/src/selfTest.ts b/src/selfTest.ts index 3930a65..f8bdcad 100644 --- a/src/selfTest.ts +++ b/src/selfTest.ts @@ -29,6 +29,7 @@ import { findNapcatImageGovernanceFindings, findQqbotNapcatCaptchaFlowFindings, findQqbotCommandServiceTestImportFindings, + findQqbotPluginPackageProductionPathFindings, findQqbotPluginSmokeImportFindings, findQqbotStatusBoundaryFindings, findTaskRecordGovernanceFindings, @@ -152,6 +153,59 @@ export async function runSelfTest(): Promise { ); } + const qqbotPluginProductionPathFindings = + findQqbotPluginPackageProductionPathFindings( + [ + "const DEFAULT_BUILTIN_PACKAGE_ROOT_SEGMENTS = [", + " ['src', 'modules', 'qqbot', 'plugins'],", + "];", + "private resolveEntryFile(entryFile: string) {", + " return entryFile;", + "}", + ], + { + file: "src/modules/qqbot/plugin-platform/infrastructure/integration/package/plugin-package-path-policy.service.ts", + project: "Node/kt-template-online-api", + }, + ); + if ( + !qqbotPluginProductionPathFindings.some( + (item) => + item.category === "qqbot-plugin-production-dist-root-missing", + ) || + !qqbotPluginProductionPathFindings.some( + (item) => + item.category === "qqbot-plugin-compiled-entry-fallback-missing", + ) + ) { + throw new Error("QQBot plugin production path governance self-check failed"); + } + + const qqbotPluginProductionPathValidFindings = + findQqbotPluginPackageProductionPathFindings( + [ + "const DEFAULT_BUILTIN_PACKAGE_ROOT_SEGMENTS = [", + " ['src', 'modules', 'qqbot', 'plugins'],", + " ['dist', 'modules', 'qqbot', 'plugins'],", + "];", + "private resolveCompiledEntryFile(entryFile: string): string {", + " if (entryFile.endsWith('.ts')) {", + " return `${entryFile.slice(0, -3)}.js`;", + " }", + " return entryFile;", + "}", + ], + { + file: "src/modules/qqbot/plugin-platform/infrastructure/integration/package/plugin-package-path-policy.service.ts", + project: "Node/kt-template-online-api", + }, + ); + if (qqbotPluginProductionPathValidFindings.length > 0) { + throw new Error( + "QQBot plugin production path governance valid self-check false positive", + ); + } + const qqbotStatusBoundaryFindings = [ ...findQqbotStatusBoundaryFindings( [ diff --git a/src/tools/review.ts b/src/tools/review.ts index 7c43e88..4b15a65 100644 --- a/src/tools/review.ts +++ b/src/tools/review.ts @@ -285,6 +285,71 @@ export function findQqbotPluginSmokeImportFindings( return findings; } +/** + * Finds regressions that would make descriptor-backed QQBot built-in plugins disappear from production dist images. + * @param lines - Source lines from the package path policy file being reviewed. + * @param context - Project and file identity for precise review findings. + * @returns Findings when the policy no longer supports production dist roots or compiled JavaScript entries. + */ +export function findQqbotPluginPackageProductionPathFindings( + lines: string[], + context: { + file: string; + project: string; + }, +): ReviewFinding[] { + const normalized = context.file.replaceAll("\\", "/"); + if ( + context.project !== "Node/kt-template-online-api" || + normalized !== + "src/modules/qqbot/plugin-platform/infrastructure/integration/package/plugin-package-path-policy.service.ts" + ) { + return []; + } + + const content = lines.join("\n"); + const findings: ReviewFinding[] = []; + const hasProductionDistRoot = + content.includes("'dist'") && + content.includes("'modules'") && + content.includes("'qqbot'") && + content.includes("'plugins'"); + const hasCompiledEntryFallback = + content.includes("resolveCompiledEntryFile") && + content.includes("entryFile.endsWith('.ts')") && + content.includes("}.js`"); + + if (!hasProductionDistRoot) { + findings.push({ + category: "qqbot-plugin-production-dist-root-missing", + file: context.file, + line: 1, + level: "P2", + message: + "QQBot 插件包路径策略缺少生产 dist 根,生产镜像只复制 dist 时内置插件会发现为空。", + project: context.project, + suggestion: + "默认受控根应在源码根不存在时扫描 dist/modules/qqbot/plugins,并保留源码态优先顺序。", + }); + } + + if (!hasCompiledEntryFallback) { + findings.push({ + category: "qqbot-plugin-compiled-entry-fallback-missing", + file: context.file, + line: 1, + level: "P2", + message: + "QQBot 插件 manifest 的 src/index.ts entry 缺少生产编译后 .js sibling fallback。", + project: context.project, + suggestion: + "entry 仍应保持源码结构声明,但生产运行时要在同一 package root 内解析到编译后的 src/index.js。", + }); + } + + return findings; +} + export function findQqbotStatusBoundaryFindings( lines: string[], context: { @@ -856,6 +921,10 @@ function collectProjectContentFindings( file, project: project.relativePath, }), + ...findQqbotPluginPackageProductionPathFindings(lines, { + file, + project: project.relativePath, + }), ...findQqbotStatusBoundaryFindings(lines, { file, project: project.relativePath,