#!/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); } // currently we can only draft notes for patch releases. if (parts[2] === 0) { process.exit(0); } let priorVersion = [parts[0], parts[1], parts[2] - 1].join("."); let suffix = channel == "preview" ? "-pre" : ""; let [tag, priorTag] = [`v${version}${suffix}`, `v${priorVersion}${suffix}`]; try { execFileSync("rm", ["-rf", "target/shallow_clone"]); execFileSync("git", [ "clone", "https://github.com/zed-industries/zed", "target/shallow_clone", "--filter=tree:0", "--no-checkout", "--branch", tag, "--depth", 100, ]); execFileSync("git", ["-C", "target/shallow_clone", "rev-parse", "--verify", tag]); try { execFileSync("git", ["-C", "target/shallow_clone", "rev-parse", "--verify", priorTag]); } catch (e) { console.error(`Prior tag ${priorTag} not found`); process.exit(0); } } catch (e) { console.error(e.stderr.toString()); process.exit(1); } 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); } } if (releaseNotes.length === 0) { const compareUrl = `https://github.com/zed-industries/zed/compare/${priorTag}...${tag}#commits_bucket`; console.log(`No public-facing changes in this release. [View the commits](${compareUrl}).\n`); } else { console.log(releaseNotes.join("\n") + "\n"); }} function getCommits(oldTag, newTag) { const pullRequestNumbers = execFileSync( "git", ["-C", "target/shallow_clone", "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;}
#!/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);
}
// currently we can only draft notes for patch releases.
if (parts[2] === 0) {
process.exit(0);
}
let priorVersion = [parts[0], parts[1], parts[2] - 1].join(".");
let suffix = channel == "preview" ? "-pre" : "";
let [tag, priorTag] = [`v${version}${suffix}`, `v${priorVersion}${suffix}`];
try {
execFileSync("rm", ["-rf", "target/shallow_clone"]);
execFileSync("git", [
"clone",
"https://github.com/zed-industries/zed",
"target/shallow_clone",
"--filter=tree:0",
"--no-checkout",
"--branch",
tag,
"--depth",
100,
]);
execFileSync("git", ["-C", "target/shallow_clone", "rev-parse", "--verify", tag]);
try {
execFileSync("git", ["-C", "target/shallow_clone", "rev-parse", "--verify", priorTag]);
} catch (e) {
console.error(`Prior tag ${priorTag} not found`);
process.exit(0);
}
} catch (e) {
console.error(e.stderr.toString());
process.exit(1);
}
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);
}
}
if (releaseNotes.length === 0) {
const compareUrl = `https://github.com/zed-industries/zed/compare/${priorTag}...${tag}#commits_bucket`;
console.log(`No public-facing changes in this release. [View the commits](${compareUrl}).\n`);
} else {
console.log(releaseNotes.join("\n") + "\n");
}
}
function getCommits(oldTag, newTag) {
const pullRequestNumbers = execFileSync(
"git",
["-C", "target/shallow_clone", "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;
}