Skip to content

Commit

Permalink
General fixes for release-tool (#7238)
Browse files Browse the repository at this point in the history
* General fixes for release-tool

Signed-off-by: Sebastian Malton <sebastian@malton.name>

* Revert change to number of PRs retrieved

Signed-off-by: Sebastian Malton <sebastian@malton.name>

---------

Signed-off-by: Sebastian Malton <sebastian@malton.name>
  • Loading branch information
Nokel81 committed Mar 1, 2023
1 parent b74cc14 commit 3b9903d
Showing 1 changed file with 69 additions and 68 deletions.
137 changes: 69 additions & 68 deletions packages/release-tool/src/index.ts
Expand Up @@ -5,7 +5,7 @@
*/
import assert from "assert";
import chalk from "chalk";
import child_process from "child_process";
import child_process, { spawn } from "child_process";
import { readFile } from "fs/promises";
import inquirer from "inquirer";
import { createInterface, ReadLine } from "readline";
Expand All @@ -16,9 +16,21 @@ type SemVer = semver.SemVer;

const { SemVer } = semver;
const exec = promisify(child_process.exec);
const spawn = promisify(child_process.spawn);
const execFile = promisify(child_process.execFile);

async function pipeExecFile(file: string, args: string[], opts?: { stdin: string }) {
const p = execFile(file, args);

p.child.stdout?.pipe(process.stdout);
p.child.stderr?.pipe(process.stderr);

if (opts) {
p.child.stdin?.end(opts.stdin);
}

await p;
}

interface GithubPrData {
author: {
login: string;
Expand Down Expand Up @@ -65,9 +77,35 @@ async function fetchAllGitTags(): Promise<string[]> {
.map(line => line.trim());
}

async function bumpPackageVersions(): Promise<void> {
await spawn("npm", ["run", "bump-version"], {
stdio: "inherit",
function bumpPackageVersions() {
const bumpPackages = spawn("npm", ["run", "bump-version"], {
stdio: "inherit"
});
const cleaners: (() => void)[] = [
() => bumpPackages.stdout?.unpipe(),
() => bumpPackages.stderr?.unpipe(),
];
const cleanup = () => cleaners.forEach(clean => clean());

return new Promise<void>((resolve, reject) => {
const onExit = (code: number | null) => {
cleanup();
if (code) {
reject(new Error(`"npm run bump-version" failed with code ${code}`));
} else {
resolve();
}
};
const onError = (error: Error) => {
cleanup();
reject(error);
};

bumpPackages.once("error", onError);
cleaners.push(() => bumpPackages.off("error", onError));

bumpPackages.once("exit", onExit);
cleaners.push(() => bumpPackages.off("exit", onExit));
});
}

Expand Down Expand Up @@ -110,30 +148,22 @@ function formatSemverForMilestone(version: SemVer): string {
async function createReleaseBranchAndCommit(prBase: string, version: SemVer, prBody: string): Promise<void> {
const prBranch = `release/v${version.format()}`;

await spawn("git", ["checkout", "-b", prBranch], {
stdio: "inherit",
});
await spawn("git", ["add", "lerna.json", "packages/*/package.json"], {
stdio: "inherit",
});
await spawn("git", ["commit", "-sm", `"Release ${version.format()}"`], {
stdio: "inherit",
});
await spawn("git", ["push", "--set-upstream", "origin", prBranch], {
stdio: "inherit",
});
await pipeExecFile("git", ["checkout", "-b", prBranch]);
await pipeExecFile("git", ["add", "lerna.json", "packages/*/package.json"]);
await pipeExecFile("git", ["commit", "-sm", `Release ${version.format()}`]);
await pipeExecFile("git", ["push", "--set-upstream", "origin", prBranch]);

await spawn("gh", [
await pipeExecFile("gh", [
"pr",
"create",
"--base", prBase,
"--title", `Release ${version.format()}`,
"--label", "skip-changelog",
"--label", "release",
"--milestone", formatSemverForMilestone(version),
"--body-file", prBody,
"--body-file", "-",
], {
stdio: "inherit"
stdin: prBody,
});
}

Expand All @@ -153,7 +183,7 @@ function sortExtendedGithubPrData(left: ExtendedGithubPrData, right: ExtendedGit
}

async function getRelevantPRs(milestone: string, previousReleasedVersion: string): Promise<ExtendedGithubPrData[]> {
console.log("retreiving previous 500 PRs...");
console.log("retrieving previous 200 PRs...");

const getMergedPrsArgs = [
"gh",
Expand All @@ -167,14 +197,14 @@ async function getRelevantPRs(milestone: string, previousReleasedVersion: string

const mergedPrs = JSON.parse((await exec(getMergedPrsArgs.join(" "), { encoding: "utf-8" })).stdout) as GithubPrData[];
const milestoneRelevantPrs = mergedPrs.filter(pr => pr.milestone?.title === milestone);
const relaventPrsQuery = await Promise.all(
const relevantPrsQuery = await Promise.all(
milestoneRelevantPrs.map(async pr => ({
pr,
stdout: (await exec(`git tag v${previousReleasedVersion} --no-contains ${pr.mergeCommit.oid}`)).stdout,
})),
);

return relaventPrsQuery
return relevantPrsQuery
.filter(query => query.stdout)
.map(query => query.pr)
.filter(pr => pr.labels.every(label => label.name !== "skip-changelog"))
Expand All @@ -189,40 +219,11 @@ function formatPrEntry(pr: ExtendedGithubPrData) {
const isEnhancementPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === "enhancement");
const isBugfixPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === "bug");

const cherrypickCommitWith = (rl: ReadLine) => async (commit: string) => {
const cherryPickCommitWith = (rl: ReadLine) => async (commit: string) => {
try {
const cherryPick = child_process.spawn("git", ["cherry-pick", commit]);

cherryPick.stdout.pipe(process.stdout);
cherryPick.stderr.pipe(process.stderr);

await new Promise<void>((resolve, reject) => {
const cleaners: (() => void)[] = [];
const cleanup = () => cleaners.forEach(cleaner => cleaner());

const onExit = (code: number | null) => {
if (code) {
reject(new Error(`git cherry-pick failed with exit code ${code}`));
cleanup();
}

resolve();
cleanup();
};

cherryPick.once("exit", onExit);
cleaners.push(() => cherryPick.off("exit", onExit));

const onError = (error: Error) => {
cleanup();
reject(error);
};

cherryPick.once("error", onError);
cleaners.push(() => cherryPick.off("error", onError));
});
await pipeExecFile("git", ["cherry-pick", commit]);
} catch {
console.error(chalk.bold("Please resolve conflicts in a seperate terminal and then press enter here..."));
console.error(chalk.bold("Please resolve conflicts in a separate terminal and then press enter here..."));
await new Promise<void>(resolve => rl.once("line", () => resolve()));
}
};
Expand All @@ -249,15 +250,15 @@ async function pickWhichPRsToUse(prs: ExtendedGithubPrData[]): Promise<ExtendedG
function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrData[]): string {
const enhancementPrLines: string[] = [];
const bugPrLines: string[] = [];
const maintenencePrLines: string[] = [];
const maintenancePrLines: string[] = [];

for (const pr of prs) {
if (isEnhancementPr(pr)) {
enhancementPrLines.push(formatPrEntry(pr));
} else if (isBugfixPr(pr)) {
bugPrLines.push(formatPrEntry(pr));
} else {
maintenencePrLines.push(formatPrEntry(pr));
maintenancePrLines.push(formatPrEntry(pr));
}
}

Expand All @@ -271,32 +272,32 @@ function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrD
bugPrLines.push("");
}

if (maintenencePrLines.length > 0) {
maintenencePrLines.unshift("## 🧰 Maintenance", "");
maintenencePrLines.push("");
if (maintenancePrLines.length > 0) {
maintenancePrLines.unshift("## 🧰 Maintenance", "");
maintenancePrLines.push("");
}

return [
`## Changes since ${previousReleasedVersion}`,
"",
...enhancementPrLines,
...bugPrLines,
...maintenencePrLines,
...maintenancePrLines,
].join("\n");
}

async function cherrypickCommits(prs: ExtendedGithubPrData[]): Promise<void> {
async function cherryPickCommits(prs: ExtendedGithubPrData[]): Promise<void> {
const rl = createInterface(process.stdin);
const cherrypickCommit = cherrypickCommitWith(rl);
const cherryPickCommit = cherryPickCommitWith(rl);

for (const pr of prs) {
await cherrypickCommit(pr.mergeCommit.oid);
await cherryPickCommit(pr.mergeCommit.oid);
}

rl.close();
}

async function pickRelaventPrs(prs: ExtendedGithubPrData[], isMasterBranch: boolean): Promise<ExtendedGithubPrData[]> {
async function pickRelevantPrs(prs: ExtendedGithubPrData[], isMasterBranch: boolean): Promise<ExtendedGithubPrData[]> {
if (isMasterBranch) {
return prs;
}
Expand All @@ -307,7 +308,7 @@ async function pickRelaventPrs(prs: ExtendedGithubPrData[], isMasterBranch: bool
selectedPrs = await pickWhichPRsToUse(prs);
} while (selectedPrs.length === 0 && (console.warn("[WARNING]: must pick at least once commit"), true));

await cherrypickCommits(selectedPrs);
await cherryPickCommits(selectedPrs);

return selectedPrs;
}
Expand All @@ -326,8 +327,8 @@ async function createRelease(): Promise<void> {
}

const prMilestone = formatSemverForMilestone(await getCurrentVersionOfSubPackage("core"));
const relaventPrs = await getRelevantPRs(prMilestone, previousReleasedVersion);
const selectedPrs = await pickRelaventPrs(relaventPrs, isMasterBranch);
const relevantPrs = await getRelevantPRs(prMilestone, previousReleasedVersion);
const selectedPrs = await pickRelevantPrs(relevantPrs, isMasterBranch);
const prBody = formatChangelog(previousReleasedVersion, selectedPrs);

if (!isMasterBranch) {
Expand Down

0 comments on commit 3b9903d

Please sign in to comment.