Some checks failed
Tests / lint-tests (push) Has been cancelled
Tests / plugin-validate (push) Has been cancelled
Tests / test (ubuntu-latest, 24, shard 1/3) (push) Has been cancelled
Tests / test (ubuntu-latest, 24, shard 2/3) (push) Has been cancelled
Tests / test (ubuntu-latest, 24, shard 3/3) (push) Has been cancelled
Tests / test (ubuntu-latest, 24) (push) Has been cancelled
Tests / test (inert CI) (push) Has been cancelled
Tests / conformance test (macos-latest, 24) (push) Has been cancelled
Tests / conformance test (windows-latest, 24, shard 1/3) (push) Has been cancelled
Tests / conformance test (windows-latest, 24, shard 2/3) (push) Has been cancelled
Tests / conformance test (windows-latest, 24, shard 3/3) (push) Has been cancelled
Tests / Coverage gate (merged shards) (push) Has been cancelled
Tests / QA loop walk (smell ratchet) (push) Has been cancelled
Tests / Required tests (push) Has been cancelled
Tests / Publish emitted-baseline artifact (push) Has been cancelled
Tests / PR mergeability (push) Has been cancelled
Tests / Base branch health (push) Has been cancelled
Tests / Detect test scope (push) Has been cancelled
MSD does not use an issue tracker, and the inherited GSD community gates auto-closed PR #1 for lacking an issue-numbered title. Remove them: - workflows: require-issue-link, pr-title-validator, auto-close-unsolicited-prs, pr-template-format, close-draft-prs(-sweep), auto-label-issues, auto-branch, duplicate-check, duplicate-sweep, remove-duplicate-label - GSD PR and issue templates, plus the scripts only they used (require-issue-link-policy, pr-template-policy, pr-changed-files) and their tests - workflow lists in ci-test-scope, ci-pr-mergeability, workflow-maintainer-skip, lint-pr-check-project-dir, the docs-guard baseline and the conformance tiers - CONTRIBUTING: PR-against-next flow with no issue or template; PR titles are conventional without an issue ref; ADR/PRD files use the PR number - CONTEXT.md: drop the PR-template predicates (k328, templates-mandatory)
103 lines
4.0 KiB
JavaScript
103 lines
4.0 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Single source of truth for conventional-commit PR-title parsing.
|
|
*
|
|
* Consumed by the release-notes changelog classifier
|
|
* (scripts/release-notes/format-github-release-notes.cjs). `evaluatePrTitle`
|
|
* backed the upstream PR-title CI gate (#1549), which MSD does not run; it is
|
|
* kept so a title can still be checked against the same matcher the
|
|
* changelog buckets on.
|
|
*/
|
|
|
|
// Bucket anchors. The leading `^` is load-bearing: the changelog buckets on the
|
|
// type at the START of the title. Anything before it (e.g. a `[security] ` tag)
|
|
// defeats the anchor and silently mis-files the entry — which is exactly the
|
|
// drift the PR-title gate below rejects at open time.
|
|
const FEATURE_RE = /^feat(?:ure)?\s*(?:\(|!|:)/i;
|
|
const FIX_RE = /^fix\s*(?:\(|!|:)/i;
|
|
|
|
// A well-formed conventional header at the START of the title:
|
|
// <type>[(<scope>)][!]:
|
|
// e.g. `fix(#1542):`, `feat(#39)!:`, `fix:`, `enhance(verify-phase):`.
|
|
// Anchored with `^` so a leading tag/prefix fails to match (no `bad-prefix`).
|
|
const HEADER_RE = /^([a-z]+)(\([^)]*\))?(!)?:/i;
|
|
|
|
// An issue reference inside a scope: `(#123)`, `(#123, core)`, etc.
|
|
const ISSUE_REF_IN_SCOPE_RE = /#\d+/;
|
|
|
|
// #2716: recognized non-user-facing conventional-commit types. A title whose
|
|
// START-anchored type prefix is one of these is internal work (tests, chores,
|
|
// CI, docs, refactors, perf, reverts) and must NOT render under the user-facing
|
|
// "Enhancement" heading in release notes. Only a CLEAN prefix match qualifies —
|
|
// untyped or anchor-defeated titles fall through to the visible Enhancement
|
|
// fallback (a safety net so possibly-user-facing content is never hidden).
|
|
const NON_USER_FACING_TYPES = new Set([
|
|
'docs', 'refactor', 'test', 'ci', 'chore', 'perf', 'revert',
|
|
]);
|
|
|
|
/**
|
|
* Classify a clean conventional title into a changelog bucket.
|
|
* Callers that hold a full changelog bullet line (with a `* ` marker and a
|
|
* ` by @author` suffix) must strip those first; this operates on the title.
|
|
*
|
|
* @param {string} title
|
|
* @returns {'Feature'|'Fix'|'Enhancement'|'Internal'}
|
|
*/
|
|
function classifyBucket(title) {
|
|
const t = String(title == null ? '' : title).trim();
|
|
if (FEATURE_RE.test(t)) return 'Feature';
|
|
if (FIX_RE.test(t)) return 'Fix';
|
|
// #2716: a clean non-user-facing type prefix → Internal (omitted from user-facing
|
|
// release-note sections). The HEADER_RE anchor ensures a leading tag/prefix
|
|
// (e.g. `[security] fix(...)`) does NOT match here — those keep falling through
|
|
// to the visible Enhancement fallback.
|
|
const headerMatch = HEADER_RE.exec(t);
|
|
if (headerMatch && NON_USER_FACING_TYPES.has(headerMatch[1].toLowerCase())) {
|
|
return 'Internal';
|
|
}
|
|
return 'Enhancement';
|
|
}
|
|
|
|
const REQUIRED_FORMAT_MESSAGE = [
|
|
'PR title must follow `type(#<issue>): summary`.',
|
|
'The type must come first (no leading tags like `[security]`) and the scope',
|
|
'must carry the linked issue ref so the release changelog links to it.',
|
|
'Examples: `fix(#1542): roadmap rollback`, `feat(#39)!: drop legacy flag`,',
|
|
'`enhance(#1549): add PR-title validator`.',
|
|
].join(' ');
|
|
|
|
/**
|
|
* Validate a PR title against the convention the changelog depends on (#1549).
|
|
*
|
|
* @param {{ title?: string }} input
|
|
* @returns {{ valid: true, reason: 'valid' }
|
|
* | { valid: false, reason: 'bad-prefix'|'missing-issue-ref', message: string }}
|
|
*/
|
|
function evaluatePrTitle({ title } = {}) {
|
|
const t = String(title == null ? '' : title).trim();
|
|
|
|
const m = HEADER_RE.exec(t);
|
|
if (!m) {
|
|
// No clean `type[(scope)][!]:` at the start — covers leading tags,
|
|
// `Revert "..."`, empty, and freeform titles.
|
|
return { valid: false, reason: 'bad-prefix', message: REQUIRED_FORMAT_MESSAGE };
|
|
}
|
|
|
|
const scope = m[2]; // includes the parens, e.g. "(#1542)" — or undefined
|
|
if (!scope || !ISSUE_REF_IN_SCOPE_RE.test(scope)) {
|
|
return { valid: false, reason: 'missing-issue-ref', message: REQUIRED_FORMAT_MESSAGE };
|
|
}
|
|
|
|
return { valid: true, reason: 'valid' };
|
|
}
|
|
|
|
module.exports = {
|
|
FEATURE_RE,
|
|
FIX_RE,
|
|
HEADER_RE,
|
|
classifyBucket,
|
|
evaluatePrTitle,
|
|
REQUIRED_FORMAT_MESSAGE,
|
|
};
|