Merge remote-tracking branch 'origin/master' into worktree/ci-native-windows-20260808

This commit is contained in:
Tianyi Cui
2026-08-08 23:57:01 +08:00
199 changed files with 12103 additions and 1413 deletions

View File

@@ -13,7 +13,7 @@ updates:
cooldown:
default-days: 30
labels:
- "cleanup"
- "kind/dependency"
- "area/infra"
- package-ecosystem: "uv"
@@ -25,7 +25,7 @@ updates:
cooldown:
default-days: 30
labels:
- "cleanup"
- "kind/dependency"
- "area/infra"
- package-ecosystem: "github-actions"
@@ -37,5 +37,5 @@ updates:
cooldown:
default-days: 30
labels:
- "cleanup"
- "kind/dependency"
- "area/infra"

View File

@@ -12,6 +12,29 @@ const AUDIT_MARKER = '<!-- dsh-issue-policy -->'
const OWNER_LINE = /^Owner: @([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?)$/
const TYPES = new Set(['Idea', 'Feature', 'Bug', 'Research', 'Task'])
const PRIORITIES = ['p0', 'p1', 'p2', 'p3']
const PR_KINDS = new Set([
'kind/feature',
'kind/bug-fix',
'kind/doc',
'kind/testing',
'kind/cleanup',
'kind/dependency',
])
// Aliases removed by the unified taxonomy migration remain reserved so they cannot be recreated.
const LEGACY_LABELS = new Set([
'kind/bug',
'kind/documentation',
'feature',
'bug-fix',
'doc',
'cleanup',
'testing',
'dependencies',
'ci',
'cli',
'llm',
'web-search',
])
const TERMINAL_STATUSES = new Set(['Done', 'No action'])
const ACTIVE_STATUS_ORDER = config.statuses.filter((status) => !TERMINAL_STATUSES.has(status))
@@ -224,8 +247,14 @@ export function retainIssueReferences(references, issues) {
export function validateIssue(issue) {
const errors = validateBody(issue)
const status = issue.status
const invalidLabels = issue.labels.filter(
(label) => label.startsWith('kind/') || LEGACY_LABELS.has(label),
)
if (!/\p{Script=Han}/u.test(issue.title)) errors.push('Issue 标题必须包含中文')
if (invalidLabels.length > 0) {
errors.push(`Issue 不得使用 PR kind 或旧版标签:${invalidLabels.join(', ')}`)
}
if (
/^\s*(?:\[(?:Idea|Feature|Bug|Research|Task|P[0-3]|Inbox|Backlog|Ready|In progress|In review|Done|No action|Owner|area\/[^\]]+)[^\]]*\]|(?:Idea|Feature|Bug|Research|Task|P[0-3]|Inbox|Backlog|Ready|In progress|In review|Done|No action|Owner|area\/[^: ]+)\s*[:-])/iu.test(
issue.title,
@@ -261,12 +290,24 @@ export function validateIssue(issue) {
export function validatePullRequest(input) {
if (!requiresPullRequestPolicy(input)) return []
const errors = []
const kinds = input.labels.filter((label) => label.startsWith('kind/'))
const kinds = input.labels.filter((label) => PR_KINDS.has(label))
const unknownKinds = input.labels.filter(
(label) => label.startsWith('kind/') && !PR_KINDS.has(label) && !LEGACY_LABELS.has(label),
)
const legacyLabels = input.labels.filter((label) => LEGACY_LABELS.has(label))
const sourceLabels = input.labels.filter((label) => label.startsWith('source/'))
const priorities = input.labels.filter((label) => PRIORITIES.includes(label))
const areas = input.labels.filter((label) => label.startsWith('area/'))
if (input.references.all.length === 0) errors.push('PR 正文必须引用至少一个同仓库 Issue')
if (kinds.length !== 1) errors.push(`PR 必须恰好有一个 kind/*,当前为 ${kinds.length}`)
if (kinds.length !== 1) {
errors.push(`PR 必须恰好有一个允许的 kind/*,当前为 ${kinds.length}`)
}
if (unknownKinds.length > 0) {
errors.push(`PR 含不支持的 kind/*${unknownKinds.join(', ')}`)
}
if (legacyLabels.length > 0) errors.push(`PR 含旧版标签:${legacyLabels.join(', ')}`)
if (sourceLabels.length > 0) errors.push(`source/* 仅用于 Issue${sourceLabels.join(', ')}`)
if (priorities.length > 1) errors.push(`PR 最多有一个 p0p3当前为 ${priorities.length}`)
if (areas.length === 0) errors.push('PR 必须至少有一个 area/*')
for (const number of input.references.all) {

View File

@@ -27,6 +27,41 @@ const legalIssue = {
stateReason: null,
}
const canonicalKinds = [
'kind/feature',
'kind/bug-fix',
'kind/doc',
'kind/testing',
'kind/cleanup',
'kind/dependency',
]
// Keep an independent oracle rather than importing the implementation's reserved set.
const legacyLabels = [
'kind/bug',
'kind/documentation',
'feature',
'bug-fix',
'doc',
'cleanup',
'testing',
'dependencies',
'ci',
'cli',
'llm',
'web-search',
]
const reviewedPull = (labels) => ({
isDraft: false,
authorType: 'User',
reviewRequestCount: 1,
reviewCount: 0,
labels,
references: { all: [2], resolving: [], related: [2] },
issues: new Map([[2, { priority: null }]]),
})
test('counts only text outside details', () => {
assert.deepEqual(countVisibleUnits('支持 GitHub Project。<details>隐藏文字</details>'), {
units: 4,
@@ -92,6 +127,22 @@ test('rejects metadata prefixes in an Issue title', () => {
assert.ok(errors.includes('Issue 标题不得带 Type、Priority、Status、area 或 Owner 前缀'))
})
test('reserves PR kind and legacy labels for pull requests', () => {
for (const label of [
...canonicalKinds,
'kind/experimental',
...legacyLabels,
]) {
assert.ok(
validateIssue({ ...legalIssue, labels: [label] }).some((error) =>
error.startsWith('Issue 不得使用 PR kind 或旧版标签:'),
),
label,
)
}
assert.deepEqual(validateIssue({ ...legalIssue, labels: ['area/web', 'source/member'] }), [])
})
test('keeps terminal Status aligned with the native close reason', () => {
assert.deepEqual(
validateIssue({ ...legalIssue, status: 'Done', state: 'closed', stateReason: 'completed' }),
@@ -260,22 +311,39 @@ test('requires repository PR labels in the enforcement scope', () => {
references: { all: [2], resolving: [], related: [2] },
issues: new Map([[2, { priority: null }]]),
})
assert.ok(errors.includes('PR 必须恰好有一个 kind/*,当前为 0'))
assert.ok(errors.includes('PR 必须恰好有一个允许的 kind/*,当前为 0'))
assert.ok(errors.includes('PR 必须至少有一个 area/*'))
})
test('accepts repository-extensible kind labels', () => {
assert.deepEqual(
validatePullRequest({
isDraft: false,
authorType: 'User',
reviewRequestCount: 1,
reviewCount: 0,
labels: ['kind/dependency', 'area/infra'],
references: { all: [2], resolving: [], related: [2] },
issues: new Map([[2, { priority: null }]]),
}),
[],
test('accepts exactly the canonical kinds with extensible areas', () => {
for (const kind of canonicalKinds) {
assert.deepEqual(validatePullRequest(reviewedPull([kind, 'area/future-domain'])), [], kind)
}
})
test('rejects multiple, unknown, legacy, and Issue-source PR labels', () => {
assert.ok(
validatePullRequest(
reviewedPull(['kind/feature', 'kind/doc', 'area/web']),
).includes('PR 必须恰好有一个允许的 kind/*,当前为 2'),
)
assert.ok(
validatePullRequest(reviewedPull(['kind/experimental', 'area/web'])).includes(
'PR 含不支持的 kind/*kind/experimental',
),
)
for (const label of legacyLabels) {
assert.ok(
validatePullRequest(reviewedPull(['kind/feature', 'area/web', label])).some((error) =>
error.startsWith('PR 含旧版标签:'),
),
label,
)
}
assert.ok(
validatePullRequest(
reviewedPull(['kind/feature', 'area/web', 'source/internal-pr']),
).includes('source/* 仅用于 Issuesource/internal-pr'),
)
})