1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00
lens/scripts/create-release-pr.mjs
Sebastian Malton 7855d47951 Cleanup release PR script
- Fix bug where PR would not be created when on non-master branches

- Use more async/await

- Add more logging

Signed-off-by: Sebastian Malton <sebastian@malton.name>
2022-05-12 11:20:22 -04:00

206 lines
5.9 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
// This script creates a release PR
import { exec } from "child_process";
import commandLineArgs from "command-line-args";
import fse from "fs-extra";
import { basename } from "path";
import semver from "semver";
import { promisify } from "util";
const {
SemVer,
valid: semverValid,
rcompare: semverRcompare,
lte: semverLte,
} = semver;
const { readJson } = fse;
const execP = promisify(exec);
const options = commandLineArgs([
{
name: "type",
defaultOption: true,
},
{
name: "preid",
},
]);
const validReleaseValues = [
"major",
"minor",
"patch",
];
const validPrereleaseValues = [
"premajor",
"preminor",
"prepatch",
"prerelease",
];
const validPreidValues = [
"alpha",
"beta",
];
const errorMessages = {
noReleaseType: `No release type provided. Valid options are: ${[...validReleaseValues, ...validPrereleaseValues].join(", ")}`,
invalidRelease: (invalid) => `Invalid release type was provided (value was "${invalid}"). Valid options are: ${[...validReleaseValues, ...validPrereleaseValues].join(", ")}`,
noPreid: `No preid was provided. Use '--preid' to specify. Valid options are: ${validPreidValues.join(", ")}`,
invalidPreid: (invalid) => `Invalid preid was provided (value was "${invalid}"). Valid options are: ${validPreidValues.join(", ")}`,
wrongCwd: "It looks like you are running this script from the 'scripts' directory. This script assumes it is run from the root of the git repo",
};
if (!options.type) {
console.error(errorMessages.noReleaseType);
process.exit(1);
}
if (validReleaseValues.includes(options.type)) {
// do nothing, is valid
} else if (validPrereleaseValues.includes(options.type)) {
if (!options.preid) {
console.error(errorMessages.noPreid);
process.exit(1);
}
if (!validPreidValues.includes(options.preid)) {
console.error(errorMessages.invalidPreid(options.preid));
process.exit(1);
}
} else {
console.error(errorMessages.invalidRelease(options.type));
process.exit(1);
}
if (basename(process.cwd()) === "scripts") {
console.error(errorMessages.wrongCwd);
}
const currentPackageJson = await readJson("./package.json");
const currentVersion = new SemVer(currentPackageJson.version);
const currentVersionMilestone = `${currentVersion.major}.${currentVersion.minor}.${currentVersion.patch}`;
console.log(`current version: ${currentVersion.format()}`);
console.log("fetching tags...");
await execP("git fetch --tags --force");
const prBase = (await execP("git branch --show-current", { encoding: "utf-8" })).stdout.trim();
const tagListBody = (await execP("git tag --list", { encoding: "utf-8" })).stdout.trim();
const actualTags = tagListBody.split(/\r?\n/).map(line => line.trim());
const [previousReleasedVersion] = actualTags
.map(semverValid)
.filter(Boolean)
.sort(semverRcompare)
.filter(version => semverLte(version, currentVersion));
const npmVersionArgs = [
"npm",
"version",
options.type,
];
if (options.preid) {
npmVersionArgs.push(`--preid=${options.preid}`);
}
npmVersionArgs.push("--git-tag-version false");
try {
await execP(npmVersionArgs.join(" "));
} catch (error) {
process.exit(1);
}
const newPackageJson = await readJson("./package.json");
const newVersion = new SemVer(newPackageJson.version);
const getMergedPrsArgs = [
"gh",
"pr",
"list",
"--limit=500", // Should be big enough, if not we need to release more often ;)
"--state=merged",
"--base=master",
"--json mergeCommit,title,author,labels,number,milestone",
];
console.log("retreiving last 500 PRs to create release PR body...");
const mergedPrs = JSON.parse((await execP(getMergedPrsArgs.join(" "), { encoding: "utf-8" })).stdout.trim());
const milestoneRelevantPrs = mergedPrs.filter(pr => pr.milestone && pr.milestone.title === currentVersionMilestone);
const relaventPrsQuery = await Promise.all(
milestoneRelevantPrs.map(async pr => ({
pr,
stdout: (await execP(`git tag v${previousReleasedVersion} --no-contains ${pr.mergeCommit.oid}`)).stdout,
})),
);
const relaventPrs = relaventPrsQuery
.filter(query => query.stdout)
.map(query => query.pr);
const enhancementPrLabelName = "enhancement";
const bugfixPrLabelName = "bug";
const enhancementPrs = relaventPrs.filter(pr => pr.labels.some(label => label.name === enhancementPrLabelName));
const bugfixPrs = relaventPrs.filter(pr => pr.labels.some(label => label.name === bugfixPrLabelName));
const maintenencePrs = relaventPrs.filter(pr => pr.labels.every(label => label.name !== bugfixPrLabelName && label.name !== enhancementPrLabelName));
console.log("Found:");
console.log(`${enhancementPrs.length} enhancement PRs`);
console.log(`${bugfixPrs.length} bug fix PRs`);
console.log(`${maintenencePrs.length} maintenence PRs`);
const prBodyLines = [
`## Changes since ${previousReleasedVersion}`,
"",
];
if (enhancementPrs.length > 0) {
prBodyLines.push(
"## 🚀 Features",
"",
...enhancementPrs.map(pr => `- ${pr.title} (**#${pr.number}**) https://github.com/${pr.author.login}`),
"",
);
}
if (bugfixPrs.length > 0) {
prBodyLines.push(
"## 🐛 Bug Fixes",
"",
...bugfixPrs.map(pr => `- ${pr.title} (**#${pr.number}**) https://github.com/${pr.author.login}`),
"",
);
}
if (maintenencePrs.length > 0) {
prBodyLines.push(
"## 🧰 Maintenance",
"",
...maintenencePrs.map(pr => `- ${pr.title} (**#${pr.number}**) https://github.com/${pr.author.login}`),
"",
);
}
await execP(`git push origin HEAD -u`);
const prBody = prBodyLines.join("\n");
const createPrArgs = [
"gh",
"pr",
"create",
"--base", prBase,
"--title", `"Release ${newVersion.format()}"`,
"--label", "skip-changelog",
"--milestone", currentVersionMilestone,
"--body", `"${prBody}"`,
];
const { stdout } = await execP(createPrArgs.join(" "), { stdio: "", shell: true });
console.log(`PR created: ${stdout.trim()}`);