release: 发布 v0.6.4 环境自适应与 macOS 打包

新增能力:桌面端新增 macOS 打包支持,electron-builder 增加独立 mac 配置块,
产出 dmg,按 arm64、x64 分别单独构建、不产出 universal 包;Pandoc 运行时清单
新增 darwin/arm64、darwin/x64 两个官方发行项(含独立许可证文件来源),桌面
Pandoc 候选路径与 prepare-pandoc-runtime.mjs 均已泛化为按 --platform/--arch
参数选取目标,不再只认 Windows x64。AGENTS.md 不再硬编码 Windows 绝对路径与
强制 PowerShell 语法,改为按当前 Shell 与仓库实际位置自适应。

问题修复:修复解压内置 Pandoc 时未保留 Unix 可执行权限位的问题(unzipSync
不保留压缩包内权限,已补 chmod 0o755);修复根 package.json 中
build:web-runtime/dev/desktop:dev 三个脚本的构建顺序错误
(@md-to-pdf/application 被排在其依赖的 @md-to-pdf/preview-engine 之前,在
没有历史 dist 残留的全新环境中会导致类型解析失败);修正
packages/docx-engine/tests/pandoc-runtime.test.ts 与
apps/desktop/tests/markdown-file.test.ts 中依赖宿主 OS 或路径分隔符的测试
断言,避免这些用例只能在特定平台上通过。

验证结果:docx-engine 包 93 项测试、desktop 包 62 项测试(61 通过 + 1 项
Windows 专属用例按预期跳过)均通过;全项目类型检查、生产构建通过,
git diff --check 无空白错误。跳过依赖 Git 忽略的真实客户文档语料(tmp/)的
test:docx-real-world-corpus 与 test:docx-release-gate-suites 后,其余全部
工作区测试均通过;这两步需要接入真实语料的机器上单独执行。本机 macOS
(Apple Silicon)实测 npm run package:mac:arm64 全链路成功,内置 Pandoc
3.9.0.2 完成下载、哈希校验与真实 DOCX 冒烟转换;实际截图确认窗口、macOS
原生菜单栏、编辑器工具栏、中文字体与实时预览渲染正常。

兼容与部署:版本统一为 0.6.4。本版本仅完成开发基础设施与验证,未构建正式
发行文件:没有产出真实签名的 dmg、没有重新构建 Windows 安装包,也没有重新
构建 Docker 镜像;正式 macOS 发行产物与 Windows/Docker 同步验证留待 DOCX
发布门禁阶段完成后一并发布。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5N6pcbjV4jVfXrcH3NcLP
This commit is contained in:
SkyJourney
2026-08-26 19:32:38 +08:00
co-authored by Claude Sonnet 5
parent effeca7ca8
commit c4df499680
22 changed files with 412 additions and 148 deletions
@@ -2,13 +2,18 @@ import { DOCX_PANDOC_VERSION } from "@md-to-pdf/core";
import runtimeManifest from "./pandoc-runtime.json" with { type: "json" };
export interface PandocRuntimeArtifact {
platform: "win32" | "linux";
architecture: "x64";
platform: "win32" | "linux" | "darwin";
architecture: "x64" | "arm64";
archiveType: "zip" | "tar.gz";
downloadUrl: string;
sha256: string;
executableRelativePath: string;
licenseFiles: readonly string[];
/**
* 部分平台的官方归档不随附许可证文件(如 macOS zip),需要单独声明
* 每个许可证文件的独立下载来源;缺省时许可证文件从归档内部提取。
*/
licenseSource?: Readonly<Record<string, { downloadUrl: string }>>;
files?: Readonly<
Record<string, { bytes: number; sha256: string }>
>;
@@ -35,6 +35,68 @@
"sha256": "A69ABFABABDA8A56969A254B09F9553A7BE89DDEC00D4E0FE9FD585D71A67508",
"executableRelativePath": "bin/pandoc",
"licenseFiles": ["COPYING.md", "COPYRIGHT"]
},
{
"platform": "darwin",
"architecture": "arm64",
"archiveType": "zip",
"downloadUrl": "https://github.com/jgm/pandoc/releases/download/3.9.0.2/pandoc-3.9.0.2-arm64-macOS.zip",
"sha256": "6E9ECA844076BCBB599BBEEBBBA78A70F93B5307782B85C2C272872812C88875",
"executableRelativePath": "pandoc-3.9.0.2-arm64/bin/pandoc",
"licenseFiles": ["COPYING.md", "COPYRIGHT"],
"licenseSource": {
"COPYING.md": {
"downloadUrl": "https://raw.githubusercontent.com/jgm/pandoc/3.9.0.2/COPYING.md"
},
"COPYRIGHT": {
"downloadUrl": "https://raw.githubusercontent.com/jgm/pandoc/3.9.0.2/COPYRIGHT"
}
},
"files": {
"pandoc": {
"bytes": 187727808,
"sha256": "901A9D2B3D1484E008CBCE7A11739E8C9C22161C4A4222589ED2C53C96B8A55E"
},
"COPYING.md": {
"bytes": 17787,
"sha256": "9D56CAC92294E206AF026A5502BEE0FED77200B08B51EC28AA63C9EFDA4DCFDD"
},
"COPYRIGHT": {
"bytes": 9598,
"sha256": "842E33EF01625E93F85BEBB8BAC83AA570186B7AA77A09971257CC29F8F60740"
}
}
},
{
"platform": "darwin",
"architecture": "x64",
"archiveType": "zip",
"downloadUrl": "https://github.com/jgm/pandoc/releases/download/3.9.0.2/pandoc-3.9.0.2-x86_64-macOS.zip",
"sha256": "B9FBCEABCCBC8F34AC021A50483FC32F8160568D0B4B2C22D81BB29E3054FD82",
"executableRelativePath": "pandoc-3.9.0.2-x86_64/bin/pandoc",
"licenseFiles": ["COPYING.md", "COPYRIGHT"],
"licenseSource": {
"COPYING.md": {
"downloadUrl": "https://raw.githubusercontent.com/jgm/pandoc/3.9.0.2/COPYING.md"
},
"COPYRIGHT": {
"downloadUrl": "https://raw.githubusercontent.com/jgm/pandoc/3.9.0.2/COPYRIGHT"
}
},
"files": {
"pandoc": {
"bytes": 119823904,
"sha256": "539A4D539386E62EA7DBD6F0B7C0F76DFADAA78D57E47472849B1C878F8A0A6B"
},
"COPYING.md": {
"bytes": 17787,
"sha256": "9D56CAC92294E206AF026A5502BEE0FED77200B08B51EC28AA63C9EFDA4DCFDD"
},
"COPYRIGHT": {
"bytes": 9598,
"sha256": "842E33EF01625E93F85BEBB8BAC83AA570186B7AA77A09971257CC29F8F60740"
}
}
}
]
}
@@ -140,6 +140,22 @@ function createCandidates(options: PandocRuntimeOptions) {
exclusive: false
});
}
if (
platform === "darwin" &&
(architecture === "arm64" || architecture === "x64") &&
options.desktopResourcesPath
) {
candidates.push({
executablePath: path.join(
options.desktopResourcesPath,
"pandoc",
DOCX_PANDOC_VERSION,
`darwin-${architecture}`,
"pandoc"
),
exclusive: false
});
}
if (platform === "linux" && architecture === "x64") {
candidates.push({
executablePath: `/opt/pandoc/${DOCX_PANDOC_VERSION}/bin/pandoc`,
@@ -1,3 +1,4 @@
import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import { zipSync } from "fflate";
import {
@@ -118,7 +119,34 @@ describe("Pandoc 运行时探测", () => {
executablePath: "pandoc.exe"
});
expect(runner.mock.calls[0]?.[0]).toContain(
`pandoc\\${DOCX_PANDOC_VERSION}\\windows-x86_64\\pandoc.exe`
path.join("pandoc", DOCX_PANDOC_VERSION, "windows-x86_64", "pandoc.exe")
);
});
it("macOS 内置路径不存在时回退到 PATH", async () => {
const runner = vi
.fn<PandocProcessRunner>()
.mockResolvedValueOnce(
processResult({
outcome: "not-found",
exitCode: null,
stdout: new Uint8Array()
})
)
.mockResolvedValueOnce(processResult());
const resolution = await probePandocRuntime({
platform: "darwin",
architecture: "arm64",
desktopResourcesPath: "/Applications/MorphDoc.app/Contents/Resources",
environment: {},
runner
});
expect(resolution).toMatchObject({
capability: { status: "available" },
executablePath: "pandoc"
});
expect(runner.mock.calls[0]?.[0]).toContain(
path.join("pandoc", DOCX_PANDOC_VERSION, "darwin-arm64", "pandoc")
);
});