120 lines · 3.2 KB · Text
RawDownload
#!/usr/bin/env node --redirect-warnings=/dev/null const { execFileSync } = require("child_process"); main(); async function main() {  let version = process.argv[2];  let channel = process.argv[3];  let parts = version.split(".");   if (    process.argv.length != 4 ||    parts.length != 3 ||    parts.find((part) => isNaN(part)) != null ||    (channel != "stable" && channel != "preview")  ) {    console.log("Usage: draft-release-notes <version> {stable|preview}");    process.exit(1);  }   let priorVersion = [parts[0], parts[1], parts[2] - 1].join(".");  let suffix = "";   if (channel == "preview") {    suffix = "-pre";    if (parts[2] == 0) {      priorVersion = [parts[0], parts[1] - 1, 0].join(".");    }  } else if (!ensureTag(`v${priorVersion}`)) {    console.log("Copy the release notes from preview.");    process.exit(0);  }   let [tag, priorTag] = [`v${version}${suffix}`, `v${priorVersion}${suffix}`];   if (!ensureTag(tag) || !ensureTag(priorTag)) {    console.log("Could not draft release notes, missing a tag:", tag, priorTag);    process.exit(0);  }   const newCommits = getCommits(priorTag, tag);   let releaseNotes = [];  let missing = [];  let skipped = [];   for (const commit of newCommits) {    let link = "https://github.com/zed-industries/zed/pull/" + commit.pr;    let notes = commit.releaseNotes;    if (commit.pr == "") {      link = "https://github.com/zed-industries/zed/commits/" + commit.hash;    } else if (!notes.includes("zed-industries/zed/issues")) {      notes = notes + " ([#" + commit.pr + "](" + link + "))";    }     if (commit.releaseNotes == "") {      missing.push("- MISSING " + commit.firstLine + " " + link);    } else if (commit.releaseNotes.startsWith("- N/A")) {      skipped.push("- N/A " + commit.firstLine + " " + link);    } else {      releaseNotes.push(notes);    }  }   console.log(releaseNotes.join("\n") + "\n");  console.log("<!-- ");  console.log(missing.join("\n"));  console.log(skipped.join("\n"));  console.log("-->");} function getCommits(oldTag, newTag) {  const pullRequestNumbers = execFileSync(    "git",    ["log", `${oldTag}..${newTag}`, "--format=DIVIDER\n%H|||%B"],    { encoding: "utf8" },  )    .replace(/\r\n/g, "\n")    .split("DIVIDER\n")    .filter((commit) => commit.length > 0)    .map((commit) => {      let [hash, firstLine] = commit.split("\n")[0].split("|||");      let cherryPick = firstLine.match(/\(cherry-pick #([0-9]+)\)/)?.[1] || "";      let pr = firstLine.match(/\(#(\d+)\)$/)?.[1] || "";      let releaseNotes = (commit.split(/Release notes:.*\n/i)[1] || "")        .split("\n\n")[0]        .trim()        .replace(/\n(?![\n-])/g, " ");       if (releaseNotes.includes("<public_issue_number_if_exists>")) {        releaseNotes = "";      }       return {        hash,        pr,        cherryPick,        releaseNotes,        firstLine,      };    });   return pullRequestNumbers;} function ensureTag(tag) {  try {    execFileSync("git", ["rev-parse", "--verify", tag]);    return true;  } catch (e) {    try {      execFileSync("git"[("fetch", "origin", "--shallow-exclude", tag)]);      execFileSync("git"[("fetch", "origin", "--deepen", "1")]);      return true;    } catch (e) {      return false;    }  }}