Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions docs/release-notes/TEMPLATE-zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,19 @@
2. <说明是否需要配置、数据库或备份迁移。>
3. <说明升级后的首次检查步骤。>

## 🛡 安全保障
## 🔒 数据处理方式

- <说明本版本保持不变的认证和消息边界。>
- <说明备份、事务和恢复边界。>
- <说明平台或路径安全限制。>
- <用用户能理解的方式说明数据会如何处理、是否创建备份,以及重要的数据边界。>
- <只保留会影响用户操作的限制,不罗列内部检查、审计或流程。>

## ⚙️ 自动化接口(如适用)

<先说明普通用户是否需要本节,再说明协议变化和兼容性。没有变化时可省略本节。>

## ⚠️ 重要说明
## 🗂 使用提示

- <列出会直接影响使用结果、但不能由工具自动解决的限制。>
- <不要把普通实现细节放在这里。>
- <列出会直接影响使用结果、但工具无法自动解决的限制。>
- <给出遇到问题时下一步该做什么。>

## 🔍 验证结果

Expand Down
35 changes: 18 additions & 17 deletions scripts/read-release-metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,18 @@ import { fileURLToPath, pathToFileURL } from "node:url";
const RELEASE_TAG_PATTERN =
/^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
const TITLE_PATTERN = /^<!-- release-title: ([^\r\n]+) -->$/gm;
const REQUIRED_ANNOUNCEMENT_MARKERS = [
"## 📦 下载",
"## ⬆️ 升级说明",
"## 🛡 安全保障",
"## ⚠️ 重要说明",
"## 🔍 验证结果",
"SmartScreen",
"SHA-256",
"`auth.json`",
"`updated_at`",
"`encrypted_content`",
"回滚",
"WSL UNC",
const REQUIRED_ANNOUNCEMENT_SECTION_GROUPS = [
{ label: "下载", markers: ["## 📦 下载"] },
{ label: "升级说明", markers: ["## ⬆️ 升级说明"] },
{
label: "使用说明",
markers: ["## ⚠️ 重要说明", "## 🗂 会话同步怎么处理", "## 使用提示"],
},
{
label: "数据处理方式",
markers: ["## 🛡 安全保障", "## 🔒 数据处理方式"],
},
{ label: "验证", markers: ["## 🔍 验证结果", "## 🔍 本版验证"] },
];

function displayPath(rootDir, targetPath) {
Expand Down Expand Up @@ -57,12 +56,14 @@ export function readReleaseMetadata({ rootDir, tag }) {
if (/\b(?:TODO|TBD)\b/.test(body)) {
throw new Error(`${relativeBodyPath} still contains a TODO or TBD placeholder.`);
}
const missingMarkers = REQUIRED_ANNOUNCEMENT_MARKERS.filter(
(marker) => !body.includes(marker),
const missingSections = REQUIRED_ANNOUNCEMENT_SECTION_GROUPS.filter(
({ markers }) => !markers.some((marker) => body.includes(marker)),
).map(
({ label }) => label,
);
if (missingMarkers.length > 0) {
if (missingSections.length > 0) {
throw new Error(
`${relativeBodyPath} is missing required release or safety content: ${missingMarkers.join(", ")}`,
`${relativeBodyPath} is missing required user-facing sections: ${missingSections.join(", ")}`,
);
}

Expand Down
43 changes: 40 additions & 3 deletions test/release-metadata.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ test("reads the current repository Chinese release metadata", () => {
assert.equal(result.relativeBodyPath, "docs/release-notes/v0.5.0-zh.md");
});

test("reads the current user-facing v1.0.3 announcement", () => {
const testDirectory = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(testDirectory, "..");

const result = readReleaseMetadata({ rootDir, tag: "v1.0.3" });

assert.equal(result.title, "v1.0.3 - 会话同步与更新体验改进");
});

test("reads one validated release title and body path", () =>
withFixture((rootDir) => {
writeAnnouncement(
Expand Down Expand Up @@ -114,14 +123,42 @@ test("rejects duplicate, mismatched, and placeholder release metadata", () =>
);
}));

test("rejects a release announcement missing required safety content", () =>
test("accepts user-facing sections without fixed internal boilerplate", () =>
withFixture((rootDir) => {
writeAnnouncement(
rootDir,
"v1.2.3",
`<!-- release-title: v1.2.3 - 用户向标题 -->

${"发布内容。".repeat(50)}

## 📦 下载
下载说明。

## ⬆️ 升级说明
升级说明。

## 🗂 会话同步怎么处理
使用说明。

## 🔒 数据处理方式
数据说明。

## 🔍 本版验证
验证说明。`,
);

assert.doesNotThrow(() => readReleaseMetadata({ rootDir, tag: "v1.2.3" }));
}));

test("rejects a release announcement missing required user-facing sections", () =>
withFixture((rootDir) => {
const incomplete = completeAnnouncement("v1.2.3", "标题").replace("`auth.json`", "认证文件");
const incomplete = completeAnnouncement("v1.2.3", "标题").replace("## 🛡 安全保障", "## 处理细节");
writeAnnouncement(rootDir, "v1.2.3", incomplete);

assert.throws(
() => readReleaseMetadata({ rootDir, tag: "v1.2.3" }),
/missing required release or safety content: `auth\.json`/,
/missing required user-facing sections: 数据处理方式/,
);
}));

Expand Down
Loading