Add get stable channel release notes script (#36969)

Joseph T. Lyons created

Release Notes:

- N/A

Change summary

script/get-preview-channel-changes      |  41 +++-------
script/get-stable-channel-release-notes | 101 +++++++++++++++++++++++++++
2 files changed, 113 insertions(+), 29 deletions(-)

Detailed changes

script/get-preview-channel-changes 🔗

@@ -1,12 +1,11 @@
 #!/usr/bin/env node --redirect-warnings=/dev/null
 
 const { execFileSync } = require("child_process");
-let { GITHUB_ACCESS_TOKEN } = process.env;
+const { GITHUB_ACCESS_TOKEN } = process.env;
 const GITHUB_URL = "https://github.com";
 const SKIPPABLE_NOTE_REGEX = /^\s*-?\s*n\/?a\s*/ims;
 const PULL_REQUEST_WEB_URL = "https://github.com/zed-industries/zed/pull";
-const PULL_REQUEST_API_URL =
-  "https://api.github.com/repos/zed-industries/zed/pulls";
+const PULL_REQUEST_API_URL = "https://api.github.com/repos/zed-industries/zed/pulls";
 const DIVIDER = "-".repeat(80);
 
 main();
@@ -25,15 +24,12 @@ async function main() {
   const STAFF_MEMBERS = new Set(
     (
       await (
-        await fetch(
-          "https://api.github.com/orgs/zed-industries/teams/staff/members",
-          {
-            headers: {
-              Authorization: `token ${GITHUB_ACCESS_TOKEN}`,
-              Accept: "application/vnd.github+json",
-            },
+        await fetch("https://api.github.com/orgs/zed-industries/teams/staff/members", {
+          headers: {
+            Authorization: `token ${GITHUB_ACCESS_TOKEN}`,
+            Accept: "application/vnd.github+json",
           },
-        )
+        })
       ).json()
     ).map(({ login }) => login.toLowerCase()),
   );
@@ -44,11 +40,7 @@ async function main() {
   };
 
   // Get the last two preview tags
-  const [newTag, oldTag] = execFileSync(
-    "git",
-    ["tag", "--sort", "-committerdate"],
-    { encoding: "utf8" },
-  )
+  const [newTag, oldTag] = execFileSync("git", ["tag", "--sort", "-committerdate"], { encoding: "utf8" })
     .split("\n")
     .filter((t) => t.startsWith("v") && t.endsWith("-pre"));
 
@@ -59,14 +51,10 @@ async function main() {
   const pullRequestNumbers = getPullRequestNumbers(oldTag, newTag);
 
   // Get the PRs that were cherry-picked between main and the old tag.
-  const existingPullRequestNumbers = new Set(
-    getPullRequestNumbers("main", oldTag),
-  );
+  const existingPullRequestNumbers = new Set(getPullRequestNumbers("main", oldTag));
 
   // Filter out those existing PRs from the set of new PRs.
-  const newPullRequestNumbers = pullRequestNumbers.filter(
-    (number) => !existingPullRequestNumbers.has(number),
-  );
+  const newPullRequestNumbers = pullRequestNumbers.filter((number) => !existingPullRequestNumbers.has(number));
 
   // Fetch the pull requests from the GitHub API.
   console.log("Merged Pull requests:");
@@ -84,8 +72,7 @@ async function main() {
     const releaseNotesHeader = /^\s*Release Notes:(.+)/ims;
 
     const releaseNotes = pullRequest.body || "";
-    let contributor =
-      pullRequest.user?.login ?? "Unable to identify contributor";
+    let contributor = pullRequest.user?.login ?? "Unable to identify contributor";
     const captures = releaseNotesHeader.exec(releaseNotes);
     let notes = captures ? captures[1] : "MISSING";
     notes = notes.trim();
@@ -127,11 +114,7 @@ function getCreditString(pullRequestNumber, contributor, isStaff) {
 }
 
 function getPullRequestNumbers(oldTag, newTag) {
-  const pullRequestNumbers = execFileSync(
-    "git",
-    ["log", `${oldTag}..${newTag}`, "--oneline"],
-    { encoding: "utf8" },
-  )
+  const pullRequestNumbers = execFileSync("git", ["log", `${oldTag}..${newTag}`, "--oneline"], { encoding: "utf8" })
     .split("\n")
     .filter((line) => line.length > 0)
     .map((line) => {

script/get-stable-channel-release-notes 🔗

@@ -0,0 +1,101 @@
+#!/usr/bin/env node --redirect-warnings=/dev/null
+
+// This script should be ran before `bump-zed-minor-versions`
+
+// Prints the changelogs for all preview releases associated with the most
+// recent preview minor version.
+
+// Future TODO: Have the script perform deduplication of lines that were
+// included in both past stable and preview patches that shouldn't be mentioned
+// again in this week's stable minor release.
+
+// Future TODO: Get changelogs for latest cherry-picked commits on preview and
+// stable that didn't make it into a release, as they were cherry picked
+
+const { execFileSync } = require("child_process");
+const { GITHUB_ACCESS_TOKEN } = process.env;
+const GITHUB_TAGS_API_URL = "https://api.github.com/repos/zed-industries/zed/releases/tags";
+const DIVIDER = "-".repeat(80);
+
+main();
+
+async function main() {
+  if (!GITHUB_ACCESS_TOKEN) {
+    try {
+      GITHUB_ACCESS_TOKEN = execFileSync("gh", ["auth", "token"]).toString();
+    } catch (error) {
+      console.log(error);
+      console.log("No GITHUB_ACCESS_TOKEN and no `gh auth token`");
+      process.exit(1);
+    }
+  }
+
+  const allTags = execFileSync("git", ["tag", "--sort", "-committerdate"], { encoding: "utf8" })
+    .split("\n")
+    .filter((t) => t.length > 0);
+  const latestPreviewTag = allTags.filter((t) => t.startsWith("v") && t.endsWith("-pre"))[0];
+  const latestPreviewMinorVersion = latestPreviewTag.split(".")[1];
+  const latestPreviewTagRegex = new RegExp(`^v(\\d+)\\.(${latestPreviewMinorVersion})\\.(\\d+)-pre$`);
+
+  const parsedPreviewTags = allTags
+    .map((tag) => {
+      const match = tag.match(latestPreviewTagRegex);
+      if (match) {
+        return {
+          tag,
+          version: {
+            major: parseInt(match[1]),
+            minor: parseInt(match[2]),
+            patch: parseInt(match[3]),
+          },
+        };
+      }
+      return null;
+    })
+    .filter((item) => item !== null)
+    .sort((a, b) => a.version.patch - b.version.patch);
+
+  const matchingPreviewTags = parsedPreviewTags.map((item) => item.tag);
+
+  console.log("Fetching release information for preview tags:");
+  console.log(DIVIDER);
+
+  for (const tag of matchingPreviewTags) {
+    const releaseApiUrl = `${GITHUB_TAGS_API_URL}/${tag}`;
+
+    try {
+      const response = await fetch(releaseApiUrl, {
+        headers: {
+          Authorization: `token ${GITHUB_ACCESS_TOKEN}`,
+        },
+      });
+
+      if (!response.ok) {
+        console.log(`Failed to fetch release for ${tag}: ${response.status}`);
+        continue;
+      }
+
+      const release = await response.json();
+
+      console.log(`\nRelease: ${release.name || tag}`);
+      console.log(`Tag: ${tag}`);
+      console.log(`Published: ${release.published_at}`);
+      console.log(`URL: ${release.html_url}`);
+      console.log("\nRelease Notes:");
+      console.log(release.body || "No release notes");
+      console.log(DIVIDER);
+    } catch (error) {
+      console.log(`Error fetching release for ${tag}:`, error.message);
+    }
+  }
+
+  const patchUpdateTags = parsedPreviewTags.filter((tag) => tag.version.patch != 0).map((tag) => tag.tag);
+
+  console.log();
+  console.log("Please review the release notes associated with the following patch versions:");
+  for (const tag of patchUpdateTags) {
+    console.log(`- ${tag}`);
+  }
+  console.log("Remove items that have already been mentioned in the current published stable versions.");
+  console.log("https://github.com/zed-industries/zed/releases?q=prerelease%3Afalse&expanded=true");
+}