fix(runtime): download pinned MiSans fonts individually for 7.5

This commit is contained in:
2026-10-16 16:32:58 +08:00
parent 39cd470f92
commit 65d118a145
4 changed files with 36 additions and 31 deletions
+7 -3
View File
@@ -1,8 +1,12 @@
# DSH Runtime Release # DSH Runtime Release
## runtime-7.5:精简 MiSans 字重
本次待发布修订为 runtime-7.5,仅保留 MiSans Regular 和 Bold,从第三方 GitHub 仓库 boyan01/mi_sans_font 的固定提交下载独立 TTF 并逐个校验 SHA-256,合计约 15.73 MB,不再下载官方完整 ZIP。构建环境需要能够访问 raw.githubusercontent.com。
## runtime-7.4:文档工具、中文字体与 Chat Manager 修复 ## runtime-7.4:文档工具、中文字体与 Chat Manager 修复
本次待发布修订为 runtime-7.4:新增 Python、LibreOffice、Noto CJK 与 MiSans,并内置修复版 Chat Manager。DSH 版本仍为 0.2.0-rc.2,运行布局仍为 7,继续保留 runtime-7.3 的域名设置适配。已完成本地 AMD64 构建、19 项测试、离线启动及插件接口认证拒绝检查;ARM64 与线上面板完整删除流程尚未验收。 runtime-7.4 修订内容:新增 Python、LibreOffice、Noto CJK 与 MiSans,并内置修复版 Chat Manager。DSH 版本仍为 0.2.0-rc.2,运行布局仍为 7,继续保留 runtime-7.3 的域名设置适配。已完成本地 AMD64 构建、19 项测试、离线启动及插件接口认证拒绝检查;ARM64 与线上面板完整删除流程尚未验收。
## 内置修复版 Chat Manager ## 内置修复版 Chat Manager
@@ -18,7 +22,7 @@ Runtime 镜像包含 Debian Bookworm 的 Python 3、pip、venv(`python` 指向
这些依赖在镜像构建时安装;已有实例需要重建镜像并升级后才生效。 这些依赖在镜像构建时安装;已有实例需要重建镜像并升级后才生效。
另包含小米 MiSans 简体中文字体的 10 个静态 TTF 字重,保留 Noto CJK,不修改默认字体优先级。构建时从小米官方下载并校验固定 SHA-256,仅安装原始 TTF 文件;上游包变化会令构建失败,需审核后更新校验值。字体及声明位于 `/usr/local/share/fonts/misans`。MiSans 使用小米专有免费商用许可,并非开源字体许可;安装使用及对外分发镜像前应阅读[官方许可协议](https://hyperos.mi.com/font/zh/download/),不得单独分发或售卖字体。 另包含小米 MiSans 简体中文字体的 Regular 和 Bold 两个静态 TTF 字重,保留 Noto CJK,不修改默认字体优先级。构建时从第三方镜像 boyan01/mi_sans_font 的固定提交 68f3ec3fee5830ce8a1d955d462fa0df401a5341 下载两个 TTF 并校验各自 SHA-256;镜像文件与当前官网 ZIP 不同,不保证为最新版本,GitHub 仓库许可不替代小米字体许可。字体及声明位于 `/usr/local/share/fonts/misans`。MiSans 使用小米专有免费商用许可,并非开源字体许可;安装使用及对外分发镜像前应阅读[官方许可协议](https://hyperos.mi.com/font/zh/download/),不得单独分发或售卖字体。
## runtime-7.3:域名访问设置适配 ## runtime-7.3:域名访问设置适配
@@ -32,7 +36,7 @@ Runtime 镜像包含 Debian Bookworm 的 Python 3、pip、venv(`python` 指向
## 发布清单 ## 发布清单
`release.json` 是发布入口:`schema_version` 为清单格式版本,`release_id` 标识一次发布,`version` 为精确 DSH 版本,`deployment_schema` 为运行布局版本,`platforms` 为支持的平台。当前待发布配置为 `runtime-7.4`、DSH `0.2.0-rc.2`、布局 7、`linux/amd64` 和 `linux/arm64`(aarch64,不含 32 位 ARM)。 `release.json` 是发布入口:`schema_version` 为清单格式版本,`release_id` 标识一次发布,`version` 为精确 DSH 版本,`deployment_schema` 为运行布局版本,`platforms` 为支持的平台。当前待发布配置为 `runtime-7.5`、DSH `0.2.0-rc.2`、布局 7、`linux/amd64` 和 `linux/arm64`(aarch64,不含 32 位 ARM)。
ARM64 验收状态:已确认 Node/Caddy 基础镜像提供 ARM64 版本,但尚未完成 ARM64 runtime 构建及运行验收。清单声明会开放面板的对应平台构建入口,不等于兼容性证明;验收完成前不要推送发布。 ARM64 验收状态:已确认 Node/Caddy 基础镜像提供 ARM64 版本,但尚未完成 ARM64 runtime 构建及运行验收。清单声明会开放面板的对应平台构建入口,不等于兼容性证明;验收完成前不要推送发布。
+6 -1
View File
@@ -1,6 +1,11 @@
This runtime uses MiSans fonts by Xiaomi. This runtime uses MiSans fonts by Xiaomi.
Official source: https://hyperos.mi.com/font/zh/download/ Official source: https://hyperos.mi.com/font/zh/download/
Archive: https://hyperos.mi.com/font-download/MiSans.zip Download mirror (third-party, not an official Xiaomi repository):
https://github.com/boyan01/mi_sans_font/tree/68f3ec3fee5830ce8a1d955d462fa0df401a5341/lib/fonts
Files: MiSans-Regular.ttf and MiSans-Bold.ttf
These mirrored files differ from the current official ZIP; they are not claimed
to be the latest release. The mirror repository's license does not replace the
Xiaomi font license.
MiSans is subject to the MiSans Fonts Intellectual Property License Agreement MiSans is subject to the MiSans Fonts Intellectual Property License Agreement
published on the official download page. It is not an open-source font license. published on the official download page. It is not an open-source font license.
+19 -23
View File
@@ -1,33 +1,29 @@
import { createHash } from 'node:crypto'; import { createHash } from 'node:crypto';
import { mkdtempSync, writeFileSync, rmSync, mkdirSync } from 'node:fs'; import { writeFileSync, mkdirSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path'; import { join } from 'node:path';
import { execFileSync } from 'node:child_process'; import { execFileSync } from 'node:child_process';
const url = 'https://hyperos.mi.com/font-download/MiSans.zip'; // Third-party mirror, pinned to a reviewed commit; Xiaomi's font license applies.
const sha256 = 'b6aa1fc827035922612df8edf36e5609bca1c5441e25cd57572204569b7b81d9'; const base = 'https://raw.githubusercontent.com/boyan01/mi_sans_font/68f3ec3fee5830ce8a1d955d462fa0df401a5341/lib/fonts/';
const weights = ['Thin', 'ExtraLight', 'Light', 'Normal', 'Regular', 'Medium', 'Demibold', 'Semibold', 'Bold', 'Heavy']; const fonts = [
['MiSans-Regular.ttf', '7172aa1b5c703780ee12df3fe4c50481dde01f3b125a045a398000a88d032ae5', 'Regular'],
['MiSans-Bold.ttf', '250fb5c8d4657f103e332b0b948a995e45f2c159beb8d0528424e2d32ae421be', 'Bold'],
];
const destination = '/usr/local/share/fonts/misans'; const destination = '/usr/local/share/fonts/misans';
const temporary = mkdtempSync(join(tmpdir(), 'misans-'));
try {
const response = await fetch(url, { signal: AbortSignal.timeout(300000) });
if (!response.ok) throw new Error(`MiSans download failed: ${response.status}`);
const archive = Buffer.from(await response.arrayBuffer());
if (createHash('sha256').update(archive).digest('hex') !== sha256) {
throw new Error('MiSans archive checksum mismatch; review upstream changes before updating');
}
const archivePath = join(temporary, 'MiSans.zip');
writeFileSync(archivePath, archive);
mkdirSync(destination, { recursive: true }); mkdirSync(destination, { recursive: true });
for (const weight of weights) { for (const [name, sha256] of fonts) {
const name = `MiSans-${weight}.ttf`; console.log('Downloading ' + name);
// Extract only fixed, reviewed filenames; never unpack arbitrary archive paths. const response = await fetch(base + name, { signal: AbortSignal.timeout(120000) });
const font = execFileSync('python3', ['-c', 'import sys, zipfile; sys.stdout.buffer.write(zipfile.ZipFile(sys.argv[1]).read(sys.argv[2]))', archivePath, `MiSans/ttf/${name}`], { maxBuffer: 16000000 }); if (!response.ok) throw new Error('MiSans download failed: ' + name + ' HTTP ' + response.status);
const font = Buffer.from(await response.arrayBuffer());
if (createHash('sha256').update(font).digest('hex') !== sha256) {
throw new Error('MiSans checksum mismatch: ' + name);
}
writeFileSync(join(destination, name), font, { mode: 0o644 }); writeFileSync(join(destination, name), font, { mode: 0o644 });
} }
execFileSync('fc-cache', ['-f', destination], { stdio: 'inherit' }); execFileSync('fc-cache', ['-f', destination], { stdio: 'inherit' });
const family = execFileSync('fc-match', ['-f', '%{family}', 'MiSans'], { encoding: 'utf8' }); for (const [name, , style] of fonts) {
if (!family.split(',').includes('MiSans')) throw new Error(`MiSans font lookup failed: ${family}`); const file = execFileSync('fc-match', ['-f', '%{file}', 'MiSans:style=' + style], { encoding: 'utf8' });
} finally { if (file !== join(destination, name)) throw new Error('MiSans font lookup failed: ' + style + ': ' + file);
rmSync(temporary, { recursive: true, force: true });
} }
console.log('Installed MiSans Regular and Bold (SHA-256 verified)');
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"schema_version": 1, "schema_version": 1,
"release_id": "runtime-7.4", "release_id": "runtime-7.5",
"version": "0.2.0-rc.2", "version": "0.2.0-rc.2",
"deployment_schema": 7, "deployment_schema": 7,
"platforms": ["linux/amd64", "linux/arm64"] "platforms": ["linux/amd64", "linux/arm64"]