mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 09:39:25 +00:00
* refactor: split release changelogs and synchronize docs mirrors * fix: complete split changelog instructions and validation wiring * fix: complete release changelog mirror integration Regenerate existing docs mirrors within the docs-agent publication boundary, preserve one HTML release heading, and package links for oversized mirrors without changing frozen records. Update release publisher and test-routing fixtures for the shared changelog resolver. * test: align docs agent Git ownership fixtures Keep failure injection aligned with staged-index validation and mirror staging. Preserve native Git producer exit codes and verify both cached-index producers without weakening process-drain assertions. --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
64 lines
2.3 KiB
JavaScript
64 lines
2.3 KiB
JavaScript
// Shared plain-JavaScript rendering also runs in package preflight before dependency setup.
|
|
export const OPENCLAW_RELEASE_TAG_PATTERN =
|
|
/^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*(?:-(?:(?:alpha|beta)\.[1-9][0-9]*|[1-9][0-9]*))?$/u;
|
|
|
|
const CONTRIBUTION_RECORD_HEADING = "### Complete contribution record";
|
|
|
|
export function validateReleaseNotesRepository(repository) {
|
|
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u.test(repository)) {
|
|
throw new Error(`invalid GitHub repository: ${repository}`);
|
|
}
|
|
}
|
|
|
|
export function validateReleaseNotesTag(tag) {
|
|
if (!OPENCLAW_RELEASE_TAG_PATTERN.test(tag)) {
|
|
throw new Error(`invalid release tag: ${tag}`);
|
|
}
|
|
}
|
|
|
|
function tagPinnedContributionRecordUrl(repository, tag, recordPath) {
|
|
validateReleaseNotesRepository(repository);
|
|
validateReleaseNotesTag(tag);
|
|
if (!/^CHANGELOG(?:\.md|\/records\/[A-Za-z0-9.-]+\.md)$/u.test(recordPath)) {
|
|
throw new Error(`invalid contribution record path: ${recordPath}`);
|
|
}
|
|
return `https://github.com/${repository}/blob/${tag}/${recordPath}#complete-contribution-record`;
|
|
}
|
|
|
|
function headingIndexOutsideFences(markdown, heading) {
|
|
let offset = 0;
|
|
let fence;
|
|
for (const segment of markdown.split(/(?<=\n)/u)) {
|
|
const line = segment.replace(/\n$/u, "");
|
|
const fenceMatch = line.match(/^\s*(?<marker>`{3,}|~{3,})/u);
|
|
if (fenceMatch?.groups?.marker) {
|
|
const marker = fenceMatch.groups.marker;
|
|
if (!fence) {
|
|
fence = marker;
|
|
} else if (marker.charAt(0) === fence.charAt(0) && marker.length >= fence.length) {
|
|
fence = undefined;
|
|
}
|
|
} else if (!fence && line === heading) {
|
|
return offset;
|
|
}
|
|
offset += segment.length;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
export function compactReleaseNotes(section, repository, tag, recordPath = "CHANGELOG.md") {
|
|
const recordIndex = headingIndexOutsideFences(section, CONTRIBUTION_RECORD_HEADING);
|
|
if (recordIndex < 0) {
|
|
return null;
|
|
}
|
|
const editorialNotes = section.slice(0, recordIndex).trimEnd();
|
|
const contributionRecordUrl = tagPinnedContributionRecordUrl(repository, tag, recordPath);
|
|
const body = [
|
|
editorialNotes,
|
|
"",
|
|
CONTRIBUTION_RECORD_HEADING,
|
|
"",
|
|
`The full contribution record is available in the tag-pinned [CHANGELOG.md](${contributionRecordUrl}).`,
|
|
].join("\n");
|
|
return { body, editorialNotes };
|
|
}
|