diff --git a/scripts/enhance-release-pr.mjs b/scripts/enhance-release-pr.mjs index 4c1d458f76a..76aef3b4709 100644 --- a/scripts/enhance-release-pr.mjs +++ b/scripts/enhance-release-pr.mjs @@ -183,28 +183,8 @@ async function getPrForCommit(commitSha) { // --- Parse .server-changes/ files --- async function parseServerChanges() { - const dir = join(ROOT_DIR, ".server-changes"); const entries = []; - - let files; - try { - files = await fs.readdir(dir); - } catch { - return entries; - } - - // Collect file info and look up commits in parallel - const fileData = []; - for (const file of files) { - if (!file.endsWith(".md") || file === "README.md") continue; - - const filePath = join(".server-changes", file); - const content = await fs.readFile(join(dir, file), "utf-8"); - const parsed = parseFrontmatter(content); - if (!parsed.body.trim()) continue; - - fileData.push({ filePath, parsed }); - } + const fileData = await getServerChangeFileData(); // Look up commits for all files in parallel const commits = await Promise.all(fileData.map((f) => getCommitForFile(f.filePath))); @@ -232,6 +212,80 @@ async function parseServerChanges() { return entries; } +async function getServerChangeFileData() { + const liveFileData = await getLiveServerChangeFileData(); + if (liveFileData.length > 0) return liveFileData; + + // The changesets version command deletes .server-changes before this script + // enhances the release PR body. Recover those consumed files from the release + // branch diff so they still make it into the release notes handoff. + return getDeletedServerChangeFileDataFromReleaseBranch(); +} + +async function getLiveServerChangeFileData() { + const dir = join(ROOT_DIR, ".server-changes"); + + let files; + try { + files = await fs.readdir(dir); + } catch { + return []; + } + + const fileData = []; + for (const file of files.sort()) { + if (!file.endsWith(".md") || file === "README.md") continue; + + const filePath = join(".server-changes", file); + const content = await fs.readFile(join(dir, file), "utf-8"); + const parsed = parseFrontmatter(content); + if (!parsed.body.trim()) continue; + + fileData.push({ filePath, parsed }); + } + + return fileData; +} + +async function getDeletedServerChangeFileDataFromReleaseBranch() { + const baseRef = process.env.SERVER_CHANGES_BASE_REF || "origin/main"; + const releaseRef = process.env.SERVER_CHANGES_RELEASE_REF || "origin/changeset-release/main"; + + let mergeBase; + let deletedFiles; + try { + mergeBase = await gitExec(["merge-base", baseRef, releaseRef]); + deletedFiles = await gitExec([ + "diff", + "--name-only", + "--diff-filter=D", + `${mergeBase}..${releaseRef}`, + "--", + ".server-changes", + ]); + } catch { + return []; + } + + const fileData = []; + for (const filePath of deletedFiles.split("\n").filter(Boolean).sort()) { + const file = filePath.split("/").pop(); + if (!file?.endsWith(".md") || file === "README.md") continue; + + try { + const content = await gitExec(["show", `${mergeBase}:${filePath}`]); + const parsed = parseFrontmatter(content); + if (!parsed.body.trim()) continue; + fileData.push({ filePath, parsed }); + } catch { + // If an individual file cannot be recovered, skip it rather than hiding + // all other server changes. + } + } + + return fileData; +} + function parseFrontmatter(content) { const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/); if (!match) return { frontmatter: {}, body: content };