mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
General fixes for release-tool (#7238)
* 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>
This commit is contained in:
parent
d29615c089
commit
1b808cf7df
@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
import child_process from "child_process";
|
import child_process, { spawn } from "child_process";
|
||||||
import { readFile } from "fs/promises";
|
import { readFile } from "fs/promises";
|
||||||
import inquirer from "inquirer";
|
import inquirer from "inquirer";
|
||||||
import { createInterface, ReadLine } from "readline";
|
import { createInterface, ReadLine } from "readline";
|
||||||
@ -16,9 +16,21 @@ type SemVer = semver.SemVer;
|
|||||||
|
|
||||||
const { SemVer } = semver;
|
const { SemVer } = semver;
|
||||||
const exec = promisify(child_process.exec);
|
const exec = promisify(child_process.exec);
|
||||||
const spawn = promisify(child_process.spawn);
|
|
||||||
const execFile = promisify(child_process.execFile);
|
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 {
|
interface GithubPrData {
|
||||||
author: {
|
author: {
|
||||||
login: string;
|
login: string;
|
||||||
@ -65,9 +77,35 @@ async function fetchAllGitTags(): Promise<string[]> {
|
|||||||
.map(line => line.trim());
|
.map(line => line.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bumpPackageVersions(): Promise<void> {
|
function bumpPackageVersions() {
|
||||||
await spawn("npm", ["run", "bump-version"], {
|
const bumpPackages = spawn("npm", ["run", "bump-version"], {
|
||||||
stdio: "inherit",
|
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));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,20 +148,12 @@ function formatSemverForMilestone(version: SemVer): string {
|
|||||||
async function createReleaseBranchAndCommit(prBase: string, version: SemVer, prBody: string): Promise<void> {
|
async function createReleaseBranchAndCommit(prBase: string, version: SemVer, prBody: string): Promise<void> {
|
||||||
const prBranch = `release/v${version.format()}`;
|
const prBranch = `release/v${version.format()}`;
|
||||||
|
|
||||||
await spawn("git", ["checkout", "-b", prBranch], {
|
await pipeExecFile("git", ["checkout", "-b", prBranch]);
|
||||||
stdio: "inherit",
|
await pipeExecFile("git", ["add", "lerna.json", "packages/*/package.json"]);
|
||||||
});
|
await pipeExecFile("git", ["commit", "-sm", `Release ${version.format()}`]);
|
||||||
await spawn("git", ["add", "lerna.json", "packages/*/package.json"], {
|
await pipeExecFile("git", ["push", "--set-upstream", "origin", prBranch]);
|
||||||
stdio: "inherit",
|
|
||||||
});
|
|
||||||
await spawn("git", ["commit", "-sm", `"Release ${version.format()}"`], {
|
|
||||||
stdio: "inherit",
|
|
||||||
});
|
|
||||||
await spawn("git", ["push", "--set-upstream", "origin", prBranch], {
|
|
||||||
stdio: "inherit",
|
|
||||||
});
|
|
||||||
|
|
||||||
await spawn("gh", [
|
await pipeExecFile("gh", [
|
||||||
"pr",
|
"pr",
|
||||||
"create",
|
"create",
|
||||||
"--base", prBase,
|
"--base", prBase,
|
||||||
@ -131,9 +161,9 @@ async function createReleaseBranchAndCommit(prBase: string, version: SemVer, prB
|
|||||||
"--label", "skip-changelog",
|
"--label", "skip-changelog",
|
||||||
"--label", "release",
|
"--label", "release",
|
||||||
"--milestone", formatSemverForMilestone(version),
|
"--milestone", formatSemverForMilestone(version),
|
||||||
"--body-file", prBody,
|
"--body-file", "-",
|
||||||
], {
|
], {
|
||||||
stdio: "inherit"
|
stdin: prBody,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +183,7 @@ function sortExtendedGithubPrData(left: ExtendedGithubPrData, right: ExtendedGit
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getRelevantPRs(milestone: string, previousReleasedVersion: string): Promise<ExtendedGithubPrData[]> {
|
async function getRelevantPRs(milestone: string, previousReleasedVersion: string): Promise<ExtendedGithubPrData[]> {
|
||||||
console.log("retreiving previous 500 PRs...");
|
console.log("retrieving previous 200 PRs...");
|
||||||
|
|
||||||
const getMergedPrsArgs = [
|
const getMergedPrsArgs = [
|
||||||
"gh",
|
"gh",
|
||||||
@ -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 mergedPrs = JSON.parse((await exec(getMergedPrsArgs.join(" "), { encoding: "utf-8" })).stdout) as GithubPrData[];
|
||||||
const milestoneRelevantPrs = mergedPrs.filter(pr => pr.milestone?.title === milestone);
|
const milestoneRelevantPrs = mergedPrs.filter(pr => pr.milestone?.title === milestone);
|
||||||
const relaventPrsQuery = await Promise.all(
|
const relevantPrsQuery = await Promise.all(
|
||||||
milestoneRelevantPrs.map(async pr => ({
|
milestoneRelevantPrs.map(async pr => ({
|
||||||
pr,
|
pr,
|
||||||
stdout: (await exec(`git tag v${previousReleasedVersion} --no-contains ${pr.mergeCommit.oid}`)).stdout,
|
stdout: (await exec(`git tag v${previousReleasedVersion} --no-contains ${pr.mergeCommit.oid}`)).stdout,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
return relaventPrsQuery
|
return relevantPrsQuery
|
||||||
.filter(query => query.stdout)
|
.filter(query => query.stdout)
|
||||||
.map(query => query.pr)
|
.map(query => query.pr)
|
||||||
.filter(pr => pr.labels.every(label => label.name !== "skip-changelog"))
|
.filter(pr => pr.labels.every(label => label.name !== "skip-changelog"))
|
||||||
@ -189,40 +219,11 @@ function formatPrEntry(pr: ExtendedGithubPrData) {
|
|||||||
const isEnhancementPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === "enhancement");
|
const isEnhancementPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === "enhancement");
|
||||||
const isBugfixPr = (pr: ExtendedGithubPrData) => pr.labels.some(label => label.name === "bug");
|
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 {
|
try {
|
||||||
const cherryPick = child_process.spawn("git", ["cherry-pick", commit]);
|
await pipeExecFile("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));
|
|
||||||
});
|
|
||||||
} catch {
|
} 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()));
|
await new Promise<void>(resolve => rl.once("line", () => resolve()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -249,7 +250,7 @@ async function pickWhichPRsToUse(prs: ExtendedGithubPrData[]): Promise<ExtendedG
|
|||||||
function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrData[]): string {
|
function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrData[]): string {
|
||||||
const enhancementPrLines: string[] = [];
|
const enhancementPrLines: string[] = [];
|
||||||
const bugPrLines: string[] = [];
|
const bugPrLines: string[] = [];
|
||||||
const maintenencePrLines: string[] = [];
|
const maintenancePrLines: string[] = [];
|
||||||
|
|
||||||
for (const pr of prs) {
|
for (const pr of prs) {
|
||||||
if (isEnhancementPr(pr)) {
|
if (isEnhancementPr(pr)) {
|
||||||
@ -257,7 +258,7 @@ function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrD
|
|||||||
} else if (isBugfixPr(pr)) {
|
} else if (isBugfixPr(pr)) {
|
||||||
bugPrLines.push(formatPrEntry(pr));
|
bugPrLines.push(formatPrEntry(pr));
|
||||||
} else {
|
} else {
|
||||||
maintenencePrLines.push(formatPrEntry(pr));
|
maintenancePrLines.push(formatPrEntry(pr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,9 +272,9 @@ function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrD
|
|||||||
bugPrLines.push("");
|
bugPrLines.push("");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maintenencePrLines.length > 0) {
|
if (maintenancePrLines.length > 0) {
|
||||||
maintenencePrLines.unshift("## 🧰 Maintenance", "");
|
maintenancePrLines.unshift("## 🧰 Maintenance", "");
|
||||||
maintenencePrLines.push("");
|
maintenancePrLines.push("");
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -281,22 +282,22 @@ function formatChangelog(previousReleasedVersion: string, prs: ExtendedGithubPrD
|
|||||||
"",
|
"",
|
||||||
...enhancementPrLines,
|
...enhancementPrLines,
|
||||||
...bugPrLines,
|
...bugPrLines,
|
||||||
...maintenencePrLines,
|
...maintenancePrLines,
|
||||||
].join("\n");
|
].join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cherrypickCommits(prs: ExtendedGithubPrData[]): Promise<void> {
|
async function cherryPickCommits(prs: ExtendedGithubPrData[]): Promise<void> {
|
||||||
const rl = createInterface(process.stdin);
|
const rl = createInterface(process.stdin);
|
||||||
const cherrypickCommit = cherrypickCommitWith(rl);
|
const cherryPickCommit = cherryPickCommitWith(rl);
|
||||||
|
|
||||||
for (const pr of prs) {
|
for (const pr of prs) {
|
||||||
await cherrypickCommit(pr.mergeCommit.oid);
|
await cherryPickCommit(pr.mergeCommit.oid);
|
||||||
}
|
}
|
||||||
|
|
||||||
rl.close();
|
rl.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pickRelaventPrs(prs: ExtendedGithubPrData[], isMasterBranch: boolean): Promise<ExtendedGithubPrData[]> {
|
async function pickRelevantPrs(prs: ExtendedGithubPrData[], isMasterBranch: boolean): Promise<ExtendedGithubPrData[]> {
|
||||||
if (isMasterBranch) {
|
if (isMasterBranch) {
|
||||||
return prs;
|
return prs;
|
||||||
}
|
}
|
||||||
@ -307,7 +308,7 @@ async function pickRelaventPrs(prs: ExtendedGithubPrData[], isMasterBranch: bool
|
|||||||
selectedPrs = await pickWhichPRsToUse(prs);
|
selectedPrs = await pickWhichPRsToUse(prs);
|
||||||
} while (selectedPrs.length === 0 && (console.warn("[WARNING]: must pick at least once commit"), true));
|
} while (selectedPrs.length === 0 && (console.warn("[WARNING]: must pick at least once commit"), true));
|
||||||
|
|
||||||
await cherrypickCommits(selectedPrs);
|
await cherryPickCommits(selectedPrs);
|
||||||
|
|
||||||
return selectedPrs;
|
return selectedPrs;
|
||||||
}
|
}
|
||||||
@ -326,8 +327,8 @@ async function createRelease(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const prMilestone = formatSemverForMilestone(await getCurrentVersionOfSubPackage("core"));
|
const prMilestone = formatSemverForMilestone(await getCurrentVersionOfSubPackage("core"));
|
||||||
const relaventPrs = await getRelevantPRs(prMilestone, previousReleasedVersion);
|
const relevantPrs = await getRelevantPRs(prMilestone, previousReleasedVersion);
|
||||||
const selectedPrs = await pickRelaventPrs(relaventPrs, isMasterBranch);
|
const selectedPrs = await pickRelevantPrs(relevantPrs, isMasterBranch);
|
||||||
const prBody = formatChangelog(previousReleasedVersion, selectedPrs);
|
const prBody = formatChangelog(previousReleasedVersion, selectedPrs);
|
||||||
|
|
||||||
if (!isMasterBranch) {
|
if (!isMasterBranch) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user