固定桌面壳本地产物收集路径

桌面壳 release 二进制复制到根 build/native/desktop

根级原生壳门禁验证 staged 桌面二进制产物

同步原生壳方案文档和共享决策记录
This commit is contained in:
2026-06-20 21:25:12 +08:00
parent c578f6cf29
commit fa32459f12
7 changed files with 125 additions and 16 deletions
+31 -5
View File
@@ -192,6 +192,11 @@ const desktopShellNavigationSource = fs.readFileSync(
desktopShellNavigationPath,
'utf8',
);
const stageReleaseBinaryPath = new URL(
'../scripts/stage-release-binary.mjs',
import.meta.url,
);
const stageReleaseBinarySource = fs.readFileSync(stageReleaseBinaryPath, 'utf8');
const productionSourceRoots = [
new URL('../package.json', import.meta.url),
new URL('../scripts/', import.meta.url),
@@ -317,6 +322,7 @@ const expectedDesktopShellRustFiles = [
];
const expectedDesktopShellScripts = [
'check-config.mjs',
'stage-release-binary.mjs',
];
function extractCargoPackageString(source, key) {
@@ -644,12 +650,12 @@ assertDesktopSourceLayout();
for (const snippet of [
'label: \'desktop-shell-release-build-smoke\'',
"args: ['run', 'desktop-shell:build', '--', '--no-bundle']",
"label: 'desktop-shell-stage-release-binary'",
"args: ['run', 'desktop-shell:stage-release-binary']",
'function assertDesktopReleaseBinaryArtifact()',
"'apps'",
"'desktop-shell'",
"'src-tauri'",
"'target'",
"'release'",
"'build'",
"'native'",
"'desktop'",
'genarrative-desktop-shell.exe',
'genarrative-desktop-shell',
'desktop release binary is missing',
@@ -674,9 +680,28 @@ for (const snippet of [
}
}
for (const snippet of [
"'apps'",
"'desktop-shell'",
"'src-tauri'",
"'target'",
"'release'",
"'build'",
"'native'",
"'desktop'",
'fs.copyFileSync(sourcePath, stagedPath)',
'fs.chmodSync(stagedPath, sourceMode & 0o777)',
"console.log(`[desktop-shell:stage-release-binary] ${stagedPath}`)",
]) {
if (!stageReleaseBinarySource.includes(snippet)) {
throw new Error(`desktop shell release staging script missing ${snippet}`);
}
}
for (const [scriptName, expected] of Object.entries({
dev: 'WEB_PORT=3000 tauri dev',
build: 'tauri build',
'stage-release-binary': 'node scripts/stage-release-binary.mjs',
typecheck: 'node scripts/check-config.mjs',
})) {
assertPackageScript(packageConfig, 'desktop shell package', scriptName, expected);
@@ -685,6 +710,7 @@ for (const [scriptName, expected] of Object.entries({
for (const [scriptName, expected] of Object.entries({
'desktop-shell:dev': 'npm --prefix apps/desktop-shell run dev',
'desktop-shell:build': 'npm --prefix apps/desktop-shell run build --',
'desktop-shell:stage-release-binary': 'npm --prefix apps/desktop-shell run stage-release-binary',
'desktop-shell:typecheck': 'npm --prefix apps/desktop-shell run typecheck',
'desktop-shell:test': 'cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml',
})) {
@@ -0,0 +1,78 @@
import fs from 'node:fs';
import path from 'node:path';
const repoRoot = path.resolve(new URL('../../../', import.meta.url).pathname);
const releaseDir = path.join(
repoRoot,
'apps',
'desktop-shell',
'src-tauri',
'target',
'release',
);
const executableName =
process.platform === 'win32'
? 'genarrative-desktop-shell.exe'
: 'genarrative-desktop-shell';
const sourcePath = path.join(releaseDir, executableName);
const stagedDir = path.join(repoRoot, 'build', 'native', 'desktop');
const stagedPath = path.join(stagedDir, executableName);
function assertExecutable(filePath, label) {
if (!fs.existsSync(filePath)) {
throw new Error(`desktop release binary is missing: ${filePath}`);
}
const stat = fs.statSync(filePath);
if (!stat.isFile() || stat.size < 1024 * 1024) {
throw new Error(`${label} must be a real non-empty executable file`);
}
const handle = fs.openSync(filePath, 'r');
try {
const header = Buffer.alloc(8);
fs.readSync(handle, header, 0, header.length, 0);
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(`${label} 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(`${label} must be an executable Mach-O file`);
}
return;
}
if (process.platform === 'win32') {
if (header[0] !== 0x4d || header[1] !== 0x5a) {
throw new Error(`${label} must be a PE executable`);
}
}
} finally {
fs.closeSync(handle);
}
}
assertExecutable(sourcePath, 'desktop release binary');
fs.mkdirSync(stagedDir, {recursive: true});
fs.copyFileSync(sourcePath, stagedPath);
const sourceMode = fs.statSync(sourcePath).mode;
fs.chmodSync(stagedPath, sourceMode & 0o777);
assertExecutable(stagedPath, 'staged desktop release binary');
console.log(`[desktop-shell:stage-release-binary] ${stagedPath}`);