|
99 | 99 | // shipped, and the two corrected ones that replaced them. |
100 | 100 | import { execFileSync } from 'node:child_process'; |
101 | 101 | import { readFileSync, existsSync } from 'node:fs'; |
| 102 | +import { isEntrypoint } from './invoked-as.mjs'; |
102 | 103 |
|
103 | 104 | const SPEC_CHANGELOG = 'packages/spec/CHANGELOG.md'; |
104 | 105 | const SPEC_PKG = 'packages/spec/package.json'; |
@@ -837,51 +838,61 @@ function renderOk(checked, tagCorroborations) { |
837 | 838 |
|
838 | 839 | // ── Run ────────────────────────────────────────────────────────────────────── |
839 | 840 |
|
840 | | -if (process.argv.includes('--self-test')) selfTest(); |
| 841 | +function main() { |
841 | 842 |
|
842 | | -const changelogText = readFileSync(SPEC_CHANGELOG, 'utf8'); |
843 | | -const gaMajorList = gaMajors(changelogText); |
844 | | -const specVersion = JSON.parse(readFileSync(SPEC_PKG, 'utf8')).version; |
845 | | -const tagMajorList = readGaTagMajors(); |
| 843 | + const changelogText = readFileSync(SPEC_CHANGELOG, 'utf8'); |
| 844 | + const gaMajorList = gaMajors(changelogText); |
| 845 | + const specVersion = JSON.parse(readFileSync(SPEC_PKG, 'utf8')).version; |
| 846 | + const tagMajorList = readGaTagMajors(); |
846 | 847 |
|
847 | | -const problems = instrumentProblems({ gaMajorList, specVersion, tagMajorList }); |
| 848 | + const problems = instrumentProblems({ gaMajorList, specVersion, tagMajorList }); |
848 | 849 |
|
849 | | -const checked = gaMajorList.filter(inScope); |
850 | | -if (problems.length === 0 && checked.length === 0) { |
851 | | - problems.push( |
852 | | - `no GA major at or above v${SCOPE_FLOOR_MAJOR} was found in ${SPEC_CHANGELOG}, so this gate ` |
853 | | - + 'checked ZERO pages. v16 and v17 have both shipped and majors only go up, so this cannot be a ' |
854 | | - + 'true reading — a gate that silently checks nothing is worse than no gate. Fix the parse.', |
855 | | - ); |
856 | | -} |
857 | | - |
858 | | -if (problems.length === 0) { |
859 | | - const indexText = existsSync(INDEX_PATH) ? readFileSync(INDEX_PATH, 'utf8') : null; |
860 | | - if (indexText === null) { |
861 | | - problems.push(`${INDEX_PATH} is missing — there is no releases index to check entries against.`); |
| 850 | + const checked = gaMajorList.filter(inScope); |
| 851 | + if (problems.length === 0 && checked.length === 0) { |
| 852 | + problems.push( |
| 853 | + `no GA major at or above v${SCOPE_FLOOR_MAJOR} was found in ${SPEC_CHANGELOG}, so this gate ` |
| 854 | + + 'checked ZERO pages. v16 and v17 have both shipped and majors only go up, so this cannot be a ' |
| 855 | + + 'true reading — a gate that silently checks nothing is worse than no gate. Fix the parse.', |
| 856 | + ); |
862 | 857 | } |
863 | | - for (const major of checked) { |
864 | | - const pagePath = `${RELEASES_DIR}/v${major}.mdx`; |
865 | | - if (!existsSync(pagePath)) { |
| 858 | + |
| 859 | + if (problems.length === 0) { |
| 860 | + const indexText = existsSync(INDEX_PATH) ? readFileSync(INDEX_PATH, 'utf8') : null; |
| 861 | + if (indexText === null) { |
| 862 | + problems.push(`${INDEX_PATH} is missing — there is no releases index to check entries against.`); |
| 863 | + } |
| 864 | + for (const major of checked) { |
| 865 | + const pagePath = `${RELEASES_DIR}/v${major}.mdx`; |
| 866 | + if (!existsSync(pagePath)) { |
| 867 | + problems.push( |
| 868 | + `${pagePath} is missing — @objectstack/spec ${major}.x is GA but there is no release page to ` |
| 869 | + + 'check. (check:release-notes is the gate that owns page existence; this one owns what the ' |
| 870 | + + 'page SAYS.)', |
| 871 | + ); |
| 872 | + continue; |
| 873 | + } |
866 | 874 | problems.push( |
867 | | - `${pagePath} is missing — @objectstack/spec ${major}.x is GA but there is no release page to ` |
868 | | - + 'check. (check:release-notes is the gate that owns page existence; this one owns what the ' |
869 | | - + 'page SAYS.)', |
| 875 | + ...pageStatusProblems(major, pagePath, statusBlockquote(readFileSync(pagePath, 'utf8'))), |
870 | 876 | ); |
871 | | - continue; |
872 | | - } |
873 | | - problems.push( |
874 | | - ...pageStatusProblems(major, pagePath, statusBlockquote(readFileSync(pagePath, 'utf8'))), |
875 | | - ); |
876 | | - if (indexText !== null) { |
877 | | - problems.push(...indexStatusProblems(major, indexEntryLine(indexText, major))); |
| 877 | + if (indexText !== null) { |
| 878 | + problems.push(...indexStatusProblems(major, indexEntryLine(indexText, major))); |
| 879 | + } |
878 | 880 | } |
879 | 881 | } |
880 | | -} |
881 | 882 |
|
882 | | -if (problems.length > 0) { |
883 | | - console.error(renderFailure(problems)); |
884 | | - process.exit(1); |
| 883 | + if (problems.length > 0) { |
| 884 | + console.error(renderFailure(problems)); |
| 885 | + process.exit(1); |
| 886 | + } |
| 887 | + |
| 888 | + console.log(renderOk(checked, tagMajorList.filter(inScope).length)); |
885 | 889 | } |
886 | 890 |
|
887 | | -console.log(renderOk(checked, tagMajorList.filter(inScope).length)); |
| 891 | +// Guarded: this module exports its predicates (`gaMajors`, `pageStatusProblems`, |
| 892 | +// `indexStatusProblems` — check-release-section-coverage.mjs inherits the same |
| 893 | +// scope floor from it), and unguarded the whole gate ran inside any importer, |
| 894 | +// printing its verdict over theirs. |
| 895 | +if (isEntrypoint(import.meta.url)) { |
| 896 | + if (process.argv.includes('--self-test')) selfTest(); |
| 897 | + main(); |
| 898 | +} |
0 commit comments