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:
co-authored by
Claude Sonnet 5
parent
effeca7ca8
commit
c4df499680
@@ -57,8 +57,8 @@ npm run verify:pandoc-runtime -w @md-to-pdf/desktop
|
||||
```
|
||||
|
||||
版本化目录包、NSIS `Setup.exe` 和 ZIP 输出到
|
||||
`apps/desktop/out/v0.6.2/`。该目录被 Git 忽略。公司内部分发以
|
||||
`MorphDoc-0.6.2-x86_64-Setup.exe` 为正式安装包,ZIP 作为免安装
|
||||
`apps/desktop/out/v0.6.4/`。该目录被 Git 忽略。公司内部分发以
|
||||
`MorphDoc-0.6.4-x86_64-Setup.exe` 为正式安装包,ZIP 作为免安装
|
||||
辅助包;当前未配置代码签名,Windows 首次运行可能显示“未知发布者”
|
||||
提示。
|
||||
|
||||
|
||||
@@ -9,9 +9,25 @@ const windowsIcon = path.resolve(
|
||||
__dirname,
|
||||
"../../logos/desktop/windows/app.ico"
|
||||
);
|
||||
const macIcon = path.resolve(
|
||||
__dirname,
|
||||
"../../logos/desktop/macos/app.icns"
|
||||
);
|
||||
const nsisInclude = process.env.MD_TO_PDF_NSIS_INCLUDE?.trim() ||
|
||||
"build/installer.nsh";
|
||||
|
||||
// macOS 每次只构建一个架构的 dmg(arm64 或 x64),不产出 universal 包——
|
||||
// 项目内置完整 Chromium/Electron 运行时和固定版本 Pandoc,合并双架构会让单个
|
||||
// 安装包体积翻倍。目标架构通过环境变量显式声明,用于挑选对应的内置 Pandoc
|
||||
// 资源目录;不依赖 electron-builder 的 `${arch}` 路径宏(该宏在 extraResources
|
||||
// 场景下有已知的可靠性问题)。
|
||||
const macTargetArch = process.env.MD_TO_PDF_MAC_TARGET_ARCH?.trim() || "";
|
||||
if (macTargetArch && macTargetArch !== "arm64" && macTargetArch !== "x64") {
|
||||
throw new Error(
|
||||
`未知 macOS 目标架构:${macTargetArch}(仅支持 arm64 或 x64)`
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
appId: brand.appId,
|
||||
productName: brand.englishName,
|
||||
@@ -46,22 +62,12 @@ module.exports = {
|
||||
to: "font-packs",
|
||||
filter: ["**/*"]
|
||||
},
|
||||
{
|
||||
from: `.runtime/pandoc/${pandocRuntime.version}/windows-x86_64`,
|
||||
to: `pandoc/${pandocRuntime.version}/windows-x86_64`,
|
||||
filter: [
|
||||
"pandoc.exe",
|
||||
"COPYING.rtf",
|
||||
"COPYRIGHT.txt",
|
||||
"runtime-manifest.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
from: windowsIcon,
|
||||
to: "app.ico"
|
||||
},
|
||||
{
|
||||
from: "../../logos/desktop/macos/app.icns",
|
||||
from: macIcon,
|
||||
to: "app.icns"
|
||||
}
|
||||
],
|
||||
@@ -78,7 +84,19 @@ module.exports = {
|
||||
],
|
||||
icon: windowsIcon,
|
||||
executableName: brand.executableName,
|
||||
artifactName: `${brand.artifactPrefix}-${version}-x86_64.\${ext}`
|
||||
artifactName: `${brand.artifactPrefix}-${version}-x86_64.\${ext}`,
|
||||
extraResources: [
|
||||
{
|
||||
from: `.runtime/pandoc/${pandocRuntime.version}/windows-x86_64`,
|
||||
to: `pandoc/${pandocRuntime.version}/windows-x86_64`,
|
||||
filter: [
|
||||
"pandoc.exe",
|
||||
"COPYING.rtf",
|
||||
"COPYRIGHT.txt",
|
||||
"runtime-manifest.json"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
nsis: {
|
||||
oneClick: false,
|
||||
@@ -94,5 +112,36 @@ module.exports = {
|
||||
installerLanguages: ["zh_CN"],
|
||||
include: nsisInclude,
|
||||
artifactName: `${brand.artifactPrefix}-${version}-x86_64-Setup.\${ext}`
|
||||
},
|
||||
mac: {
|
||||
category: "public.app-category.productivity",
|
||||
icon: macIcon,
|
||||
executableName: brand.executableName,
|
||||
// 项目当前没有 Apple Developer 证书,显式声明不签名,与 Windows 侧
|
||||
// 一贯记录在案的"未签名"状态保持一致;后续如需公证再补齐。
|
||||
identity: null,
|
||||
target: [
|
||||
{
|
||||
target: "dmg"
|
||||
}
|
||||
],
|
||||
extraResources: macTargetArch
|
||||
? [
|
||||
{
|
||||
from: `.runtime/pandoc/${pandocRuntime.version}/darwin-${macTargetArch}`,
|
||||
to: `pandoc/${pandocRuntime.version}/darwin-${macTargetArch}`,
|
||||
filter: [
|
||||
"pandoc",
|
||||
"COPYING.md",
|
||||
"COPYRIGHT",
|
||||
"runtime-manifest.json"
|
||||
]
|
||||
}
|
||||
]
|
||||
: []
|
||||
},
|
||||
dmg: {
|
||||
title: brand.displayName,
|
||||
artifactName: `${brand.artifactPrefix}-${version}-\${arch}.\${ext}`
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@md-to-pdf/desktop",
|
||||
"version": "0.6.2",
|
||||
"version": "0.6.4",
|
||||
"private": true,
|
||||
"productName": "MorphDoc",
|
||||
"description": "墨呈桌面端:面向结构化 Markdown 的主题化文档创作与发布工具",
|
||||
@@ -11,13 +11,20 @@
|
||||
"build": "npm run clean && npm run build:main && npm run build:preload",
|
||||
"build:embedded-web": "npm --prefix ../.. run build:web-runtime",
|
||||
"prepare:pandoc": "node scripts/prepare-pandoc-runtime.mjs",
|
||||
"prepare:pandoc:mac-arm64": "node scripts/prepare-pandoc-runtime.mjs --platform=darwin --arch=arm64",
|
||||
"prepare:pandoc:mac-x64": "node scripts/prepare-pandoc-runtime.mjs --platform=darwin --arch=x64",
|
||||
"verify:pandoc-runtime": "node scripts/prepare-pandoc-runtime.mjs --check",
|
||||
"build:main": "node scripts/build-main.mjs",
|
||||
"build:preload": "esbuild src/app-preload.ts src/pdf-preload.ts --bundle --platform=node --format=cjs --external:electron --outdir=dist --out-extension:.js=.cjs",
|
||||
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
||||
"dev": "npm run build && wait-on http://localhost:5173 && electron dist/main.js --web-url=http://localhost:5173",
|
||||
"package": "npm run build:embedded-web && npm run prepare:pandoc && npm run build && electron-builder --dir --config electron-builder.config.cjs --x64",
|
||||
"package:mac:arm64": "npm run build:embedded-web && npm run prepare:pandoc:mac-arm64 && npm run build && MD_TO_PDF_MAC_TARGET_ARCH=arm64 electron-builder --dir --config electron-builder.config.cjs --mac --arm64",
|
||||
"package:mac:x64": "npm run build:embedded-web && npm run prepare:pandoc:mac-x64 && npm run build && MD_TO_PDF_MAC_TARGET_ARCH=x64 electron-builder --dir --config electron-builder.config.cjs --mac --x64",
|
||||
"make": "npm run build:embedded-web && npm run prepare:pandoc && npm run build && electron-builder --win nsis zip --config electron-builder.config.cjs --x64",
|
||||
"make:mac:arm64": "npm run build:embedded-web && npm run prepare:pandoc:mac-arm64 && npm run build && MD_TO_PDF_MAC_TARGET_ARCH=arm64 electron-builder --mac dmg --config electron-builder.config.cjs --arm64",
|
||||
"make:mac:x64": "npm run build:embedded-web && npm run prepare:pandoc:mac-x64 && npm run build && MD_TO_PDF_MAC_TARGET_ARCH=x64 electron-builder --mac dmg --config electron-builder.config.cjs --x64",
|
||||
"make:mac": "npm run make:mac:arm64 && npm run make:mac:x64",
|
||||
"test": "vitest run",
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit --pretty false",
|
||||
"verify:docx-theme-styles": "electron scripts/verify-docx-theme-styles.cjs"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import {
|
||||
access,
|
||||
chmod,
|
||||
mkdir,
|
||||
readFile,
|
||||
rename,
|
||||
@@ -43,14 +44,38 @@ async function fileExists(filePath) {
|
||||
}
|
||||
}
|
||||
|
||||
async function readRuntimeManifest() {
|
||||
function platformDirectoryName(platform, architecture) {
|
||||
if (platform === "win32" && architecture === "x64") {
|
||||
return "windows-x86_64";
|
||||
}
|
||||
if (platform === "darwin" && (architecture === "arm64" || architecture === "x64")) {
|
||||
return `darwin-${architecture}`;
|
||||
}
|
||||
throw new Error(`不支持的 Pandoc 桌面内置目标:${platform}/${architecture}`);
|
||||
}
|
||||
|
||||
/** 归档下载得到的、需要从压缩包内部提取的文件名(不含通过 licenseSource 单独下载的许可证文件)。 */
|
||||
function archiveRequiredNames(artifact) {
|
||||
const licenseSource = artifact.licenseSource ?? {};
|
||||
return [
|
||||
normalizedBaseName(artifact.executableRelativePath),
|
||||
...artifact.licenseFiles
|
||||
.filter((fileName) => !licenseSource[fileName])
|
||||
.map(normalizedBaseName)
|
||||
];
|
||||
}
|
||||
|
||||
async function readRuntimeManifest({ platform, architecture }) {
|
||||
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
||||
const artifact = manifest.artifacts?.find(
|
||||
(candidate) =>
|
||||
candidate.platform === "win32" && candidate.architecture === "x64"
|
||||
candidate.platform === platform && candidate.architecture === architecture
|
||||
);
|
||||
if (!artifact || artifact.archiveType !== "zip") {
|
||||
throw new Error("Pandoc 清单缺少 Windows x64 ZIP 发行项");
|
||||
if (!artifact) {
|
||||
throw new Error(`Pandoc 清单缺少 ${platform}/${architecture} 发行项`);
|
||||
}
|
||||
if (artifact.archiveType !== "zip") {
|
||||
throw new Error(`Pandoc 清单要求 ${platform}/${architecture} 为 ZIP 发行项`);
|
||||
}
|
||||
const requiredNames = [
|
||||
normalizedBaseName(artifact.executableRelativePath),
|
||||
@@ -92,6 +117,33 @@ async function downloadArchive(downloadUrl, destination) {
|
||||
return content;
|
||||
}
|
||||
|
||||
async function downloadLicenseSourceFile(downloadUrl) {
|
||||
const response = await fetch(downloadUrl, {
|
||||
redirect: "follow",
|
||||
signal: AbortSignal.timeout(downloadTimeoutMs)
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`Pandoc 许可证文件下载失败:HTTP ${response.status}`);
|
||||
}
|
||||
const content = new Uint8Array(await response.arrayBuffer());
|
||||
if (content.byteLength > maximumExtractedBytes) {
|
||||
throw new Error("Pandoc 许可证文件超过允许的下载大小");
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部分平台(如 macOS)的官方归档不随附许可证文件,需要按清单声明的
|
||||
* 独立来源单独下载;返回的文件与归档内提取的文件合并后一并校验哈希。
|
||||
*/
|
||||
async function loadLicenseSourceFiles(artifact) {
|
||||
const files = new Map();
|
||||
for (const [fileName, source] of Object.entries(artifact.licenseSource ?? {})) {
|
||||
files.set(fileName, await downloadLicenseSourceFile(source.downloadUrl));
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
async function loadVerifiedArchive(artifact, archivePath) {
|
||||
if (await fileExists(archivePath)) {
|
||||
const cached = new Uint8Array(await readFile(archivePath));
|
||||
@@ -122,10 +174,7 @@ async function loadVerifiedArchive(artifact, archivePath) {
|
||||
}
|
||||
|
||||
function extractRequiredFiles(archive, artifact) {
|
||||
const requiredNames = new Set([
|
||||
normalizedBaseName(artifact.executableRelativePath),
|
||||
...artifact.licenseFiles.map(normalizedBaseName)
|
||||
]);
|
||||
const requiredNames = new Set(archiveRequiredNames(artifact));
|
||||
let selectedBytes = 0;
|
||||
const entries = unzipSync(archive, {
|
||||
filter(file) {
|
||||
@@ -253,9 +302,13 @@ async function verifyPreparedRuntime(targetDirectory, manifest, artifact) {
|
||||
}
|
||||
}
|
||||
|
||||
async function preparePandocRuntime({ checkOnly = false } = {}) {
|
||||
const { manifest, artifact } = await readRuntimeManifest();
|
||||
const platformDirectory = "windows-x86_64";
|
||||
async function preparePandocRuntime({
|
||||
checkOnly = false,
|
||||
platform = process.platform,
|
||||
architecture = process.arch
|
||||
} = {}) {
|
||||
const { manifest, artifact } = await readRuntimeManifest({ platform, architecture });
|
||||
const platformDirectory = platformDirectoryName(platform, architecture);
|
||||
const runtimeRoot = path.join(desktopRoot, ".runtime", "pandoc");
|
||||
const targetDirectory = path.join(
|
||||
runtimeRoot,
|
||||
@@ -278,7 +331,10 @@ async function preparePandocRuntime({ checkOnly = false } = {}) {
|
||||
}
|
||||
const archivePath = path.join(downloadDirectory, archiveName);
|
||||
const archive = await loadVerifiedArchive(artifact, archivePath);
|
||||
const files = extractRequiredFiles(archive, artifact);
|
||||
const files = new Map([
|
||||
...extractRequiredFiles(archive, artifact),
|
||||
...await loadLicenseSourceFiles(artifact)
|
||||
]);
|
||||
const temporaryDirectory = path.join(
|
||||
runtimeRoot,
|
||||
manifest.version,
|
||||
@@ -308,6 +364,10 @@ async function preparePandocRuntime({ checkOnly = false } = {}) {
|
||||
temporaryDirectory,
|
||||
normalizedBaseName(artifact.executableRelativePath)
|
||||
);
|
||||
if (platform !== "win32") {
|
||||
// unzipSync 不保留压缩包内的可执行权限位,POSIX 平台需要手动补上。
|
||||
await chmod(executablePath, 0o755);
|
||||
}
|
||||
verifyPandocVersion(executablePath, manifest.version);
|
||||
await smokeTestPandoc(executablePath);
|
||||
await writeFile(
|
||||
@@ -315,8 +375,8 @@ async function preparePandocRuntime({ checkOnly = false } = {}) {
|
||||
`${JSON.stringify(
|
||||
{
|
||||
version: manifest.version,
|
||||
platform: "win32",
|
||||
architecture: "x64",
|
||||
platform,
|
||||
architecture,
|
||||
license: manifest.license,
|
||||
projectUrl: manifest.projectUrl,
|
||||
sourceArchiveUrl: manifest.sourceArchiveUrl,
|
||||
@@ -338,17 +398,25 @@ async function preparePandocRuntime({ checkOnly = false } = {}) {
|
||||
return targetDirectory;
|
||||
}
|
||||
|
||||
function readCliFlag(name) {
|
||||
const prefix = `--${name}=`;
|
||||
const match = process.argv.find((argument) => argument.startsWith(prefix));
|
||||
return match ? match.slice(prefix.length) : undefined;
|
||||
}
|
||||
|
||||
const isDirectInvocation =
|
||||
process.argv[1] &&
|
||||
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
||||
|
||||
if (isDirectInvocation) {
|
||||
preparePandocRuntime({ checkOnly: process.argv.includes("--check") }).catch(
|
||||
(error) => {
|
||||
console.error(error instanceof Error ? error.message : error);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
);
|
||||
preparePandocRuntime({
|
||||
checkOnly: process.argv.includes("--check"),
|
||||
platform: readCliFlag("platform") ?? process.platform,
|
||||
architecture: readCliFlag("arch") ?? process.arch
|
||||
}).catch((error) => {
|
||||
console.error(error instanceof Error ? error.message : error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||
import { mkdtemp, realpath, rm, writeFile } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
@@ -18,7 +18,11 @@ describe("桌面 Markdown 文件参数", () => {
|
||||
expect(isMarkdownFilePath("C:\\docs\\说明.txt")).toBe(false);
|
||||
});
|
||||
|
||||
it("从启动参数中提取绝对 Markdown 路径", () => {
|
||||
// findMarkdownFileArgument 依赖 Node 的 path 模块识别绝对路径,其行为
|
||||
// 随宿主 OS 而定:Windows 风格盘符路径只有在 Windows 宿主上才会被
|
||||
// 识别为绝对路径,这与真实部署一致(Windows 安装包只会在 Windows 上
|
||||
// 收到 Windows 风格的启动参数),因此该用例仅在 Windows 宿主上有意义。
|
||||
it.runIf(process.platform === "win32")("从启动参数中提取绝对 Markdown 路径", () => {
|
||||
expect(
|
||||
findMarkdownFileArgument(
|
||||
[
|
||||
@@ -83,15 +87,19 @@ describe("桌面 Markdown 文件参数", () => {
|
||||
const filePath = path.join(directory, "快照.md");
|
||||
try {
|
||||
await writeFile(filePath, "# 第一版", "utf8");
|
||||
// readMarkdownFileSnapshot 内部会 realpath 解析符号链接(例如 macOS
|
||||
// 的 /var -> /private/var),返回的是规范化路径,因此断言也需要对
|
||||
// 同一路径做 realpath,而不是直接比较 mkdtemp 拼接出的原始路径。
|
||||
const canonicalFilePath = await realpath(filePath);
|
||||
const snapshot = await readMarkdownFileSnapshot(filePath, 100);
|
||||
|
||||
expect(snapshot.document).toEqual({
|
||||
markdown: "# 第一版",
|
||||
fileName: "快照.md"
|
||||
});
|
||||
expect(snapshot.filePath).toBe(filePath);
|
||||
expect(snapshot.filePath).toBe(canonicalFilePath);
|
||||
expect(snapshot.documentKey).toBe(
|
||||
createMarkdownDocumentKey(filePath)
|
||||
createMarkdownDocumentKey(canonicalFilePath)
|
||||
);
|
||||
expect(snapshot.contentHash).toBe(
|
||||
createMarkdownContentHash("# 第一版")
|
||||
|
||||
@@ -44,8 +44,8 @@ describe("桌面发行构建链", () => {
|
||||
"@md-to-pdf/markdown-echarts",
|
||||
"@md-to-pdf/core",
|
||||
"@md-to-pdf/renderer",
|
||||
"@md-to-pdf/application",
|
||||
"@md-to-pdf/preview-engine",
|
||||
"@md-to-pdf/application",
|
||||
"@md-to-pdf/web"
|
||||
];
|
||||
|
||||
@@ -160,7 +160,7 @@ describe("桌面发行构建链", () => {
|
||||
artifactPrefix: "MorphDoc"
|
||||
});
|
||||
expect(desktopManifest).toMatchObject({
|
||||
version: "0.6.2",
|
||||
version: "0.6.4",
|
||||
productName: "MorphDoc"
|
||||
});
|
||||
expect(builderConfig).toContain("appId: brand.appId");
|
||||
|
||||
@@ -10,7 +10,7 @@ const app = buildApp({
|
||||
? {
|
||||
fontPacks: {
|
||||
roots: [fontPackRoot],
|
||||
appVersion: process.env.APP_VERSION ?? "0.6.2"
|
||||
appVersion: process.env.APP_VERSION ?? "0.6.4"
|
||||
}
|
||||
}
|
||||
: {})
|
||||
|
||||
@@ -14,7 +14,7 @@ function readProjectFile(relativePath: string) {
|
||||
return readFileSync(`${projectRoot}/${relativePath}`, "utf8");
|
||||
}
|
||||
|
||||
describe("Docker v0.6.2 部署契约", () => {
|
||||
describe("Docker v0.6.4 部署契约", () => {
|
||||
it("完整构建并复制 DOCX 运行时工作区和 Lua Filter", () => {
|
||||
const dockerfile = readProjectFile("deploy/Dockerfile");
|
||||
const workspaces = [
|
||||
@@ -82,7 +82,7 @@ describe("Docker v0.6.2 部署契约", () => {
|
||||
);
|
||||
expect(dockerfile).toContain("FONT_PACK_ROOT=/app/.local/font-packs");
|
||||
expect(compose).toContain(
|
||||
'APP_VERSION: "${MD_TO_PDF_APP_VERSION:-0.6.2}"'
|
||||
'APP_VERSION: "${MD_TO_PDF_APP_VERSION:-0.6.4}"'
|
||||
);
|
||||
expect(dockerignore.split(/\r?\n/u)).toContain(".local");
|
||||
expect(dockerignore).toContain("!output/font-packs/root/**");
|
||||
|
||||
@@ -10,8 +10,8 @@ const webRoot = fileURLToPath(new URL("../", import.meta.url));
|
||||
|
||||
describe("应用版本", () => {
|
||||
it("从统一构建版本生成标题徽标", () => {
|
||||
expect(APP_VERSION).toBe("0.6.2");
|
||||
expect(APP_VERSION_LABEL).toBe("v0.6.2");
|
||||
expect(APP_VERSION).toBe("0.6.4");
|
||||
expect(APP_VERSION_LABEL).toBe("v0.6.4");
|
||||
});
|
||||
|
||||
it("允许浏览器加载同源 Web Manifest", () => {
|
||||
|
||||
Reference in New Issue
Block a user