From aba6d14eb9fcc37d90ec4d5f51a8f684cf453b38 Mon Sep 17 00:00:00 2001 From: kdletters Date: Sat, 20 Jun 2026 06:35:10 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E9=BD=90=E6=A1=8C=E9=9D=A2=E5=A3=B3?= =?UTF-8?q?=E4=BA=A7=E7=89=A9=E9=AA=8C=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原生壳总验收检查 Tauri release 二进制产物 宿主壳方案和项目记忆同步桌面产物验收口径 --- .../shared-memory/decision-log.md | 7 +++ ...ExpoReactNative与Tauri宿主壳方案-2026-06-17.md | 2 +- scripts/check-native-shells.mjs | 59 +++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index deec11379..00478b1fe 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -2894,6 +2894,13 @@ - 影响范围:`apps/mobile-shell/app.json`、`apps/mobile-shell/scripts/check-config.mjs`、`apps/mobile-shell/scripts/check-expo-config.mjs`、原生壳方案文档。 - 验证方式:`npm run mobile-shell:typecheck`、`npm run mobile-shell:config`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。 +## 2026-06-20 桌面壳 release 二进制产物验收 + +- 背景:桌面壳统一验收已经执行 `tauri build --no-bundle`,但如果只看命令退出码,后续产物路径、二进制名称或平台输出发生漂移时,可能无法证明本机确实产出了可执行桌面壳。 +- 决策:`npm run check:native-shells` 在桌面 release build smoke 后必须检查 `apps/desktop-shell/src-tauri/target/release/genarrative-desktop-shell` 存在、体积非空,并按当前平台校验 Linux ELF / macOS Mach-O / Windows PE 文件头和可执行位。该检查不启动 GUI,也不生成平台安装包。 +- 影响范围:`scripts/check-native-shells.mjs`、原生壳方案文档。 +- 验证方式:`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。 + ## 2026-06-20 移动壳 ShellApp HostBridge 事件注入必须可执行覆盖 - 背景:Expo 移动壳声明 `host.events`、`app.lifecycle`、`network.statusChanged` 和 `navigation.canGoBack`,但 ShellApp 真实 AppState、Network 和 WebView 返回栈注入链路需要和扫码链路一样有可执行测试覆盖,不能只靠字符串门禁。 diff --git a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md index f44a4e840..9c4548a3c 100644 --- a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md +++ b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md @@ -483,7 +483,7 @@ GameBridge 禁止: 2026-06-19 追加:桌面壳 macOS 媒体权限说明进入门禁。Tauri 桌面壳仍不新增摄像头或麦克风 HostBridge method,不把系统媒体能力暴露成桌面命令;同源 H5 页面可继续使用浏览器标准 `getUserMedia` 承接儿童动作热身 Demo 的实时摄像头输入和汪汪声浪正式 runtime 的实时麦克风输入。macOS 分发包必须通过 `bundle.macOS.infoPlist="Info.plist"` 合并受控用途说明:`NSCameraUsageDescription` 只描述同源 H5 实时动作输入,`NSMicrophoneUsageDescription` 只描述同源 H5 实时声音玩法。`apps/desktop-shell/scripts/check-config.mjs` 会校验 plist 路径和两条文案,并把 `Info.plist` 纳入生产壳替身词扫描,防止桌面包缺少系统授权说明、把媒体权限扩写成通用采集能力,或在 macOS 分发配置里留下临时替身文本。 -2026-06-18 追加:桌面壳 release 构建烟测进入统一验收。`npm run check:native-shells` 会在 H5 HostBridge、Expo 壳和 Tauri 单测通过后执行 `npm run desktop-shell:build -- --no-bundle`,确认根 `dist` H5 资产、Tauri release 入口、受控命令白名单、图标和 Rust release 编译可以共同产出桌面二进制;该烟测不生成平台安装包,避免把 Linux 本机缺少的系统打包器误判为 HostBridge 回归。 +2026-06-18 追加:桌面壳 release 构建烟测进入统一验收。`npm run check:native-shells` 会在 H5 HostBridge、Expo 壳和 Tauri 单测通过后执行 `npm run desktop-shell:build -- --no-bundle`,确认根 `dist` H5 资产、Tauri release 入口、受控命令白名单、图标和 Rust release 编译可以共同产出桌面二进制;构建后还必须检查 `target/release/genarrative-desktop-shell` 存在、非空且符合当前平台可执行文件头。该烟测不生成平台安装包,避免把 Linux 本机缺少的系统打包器误判为 HostBridge 回归。 2026-06-18 追加:移动壳 Expo managed config 烟测进入统一验收。`npm run check:native-shells` 会执行 `npm run mobile-shell:config`,在 `apps/mobile-shell` 目录内调用 `expo config --type public --json`,校验 Expo CLI 实际解析结果中的包名、scheme、深链、ATS / cleartext / backup / 相机与麦克风权限、启动页、adaptive icon、插件配置和 HostBridge 版本没有漂移。 diff --git a/scripts/check-native-shells.mjs b/scripts/check-native-shells.mjs index 27dfc6b8a..fccb2c0ab 100644 --- a/scripts/check-native-shells.mjs +++ b/scripts/check-native-shells.mjs @@ -1983,6 +1983,62 @@ function assertHostBridgeLayerLayout() { ); } +function assertDesktopReleaseBinaryArtifact() { + const releaseDir = path.join( + 'apps', + 'desktop-shell', + 'src-tauri', + 'target', + 'release', + ); + const executableName = + process.platform === 'win32' + ? 'genarrative-desktop-shell.exe' + : 'genarrative-desktop-shell'; + const executablePath = path.join(releaseDir, executableName); + + if (!fs.existsSync(executablePath)) { + throw new Error(`desktop release binary is missing: ${executablePath}`); + } + + const stat = fs.statSync(executablePath); + if (!stat.isFile() || stat.size < 1024 * 1024) { + throw new Error('desktop release binary must be a real non-empty executable file'); + } + + const header = fs.readFileSync(executablePath, { start: 0, end: 7 }); + if (process.platform === 'linux') { + const isElf = + header[0] === 0x7f && + header[1] === 0x45 && + header[2] === 0x4c && + header[3] === 0x46; + if (!isElf || (stat.mode & 0o111) === 0) { + throw new Error('desktop Linux release binary must be an executable ELF file'); + } + return; + } + + if (process.platform === 'darwin') { + const machMagic = header.readUInt32BE(0); + const isMachO = + machMagic === 0xcafebabe || + machMagic === 0xcafed00d || + machMagic === 0xfeedface || + machMagic === 0xfeedfacf; + if (!isMachO || (stat.mode & 0o111) === 0) { + throw new Error('desktop macOS release binary must be an executable Mach-O file'); + } + return; + } + + if (process.platform === 'win32') { + if (header[0] !== 0x4d || header[1] !== 0x5a) { + throw new Error('desktop Windows release binary must be a PE executable'); + } + } +} + for (const step of steps) { console.log(`[check:native-shells] ${step.label}`); const result = spawnSync(step.command, step.args, { @@ -2009,6 +2065,9 @@ for (const step of steps) { } } +console.log('[check:native-shells] desktop-release-binary-artifact'); +assertDesktopReleaseBinaryArtifact(); + console.log('[check:native-shells] host-bridge-layer-layout'); assertHostBridgeLayerLayout();