@cdwr/core
Version:
A set of core utilities for the Codeware ecosystem.
299 lines (293 loc) • 9.03 kB
JavaScript
// packages/core/src/lib/release/changelogs.ts
import chalk from "chalk";
import { releaseChangelog } from "nx/release";
var changelogs = async (options) => {
const { versionData, dryRun, verbose } = options;
console.log(`${chalk.magenta.underline("Generate changelogs")}`);
try {
await releaseChangelog({
versionData,
dryRun,
verbose
});
return true;
} catch (error) {
console.error(
`Generate changelogs: ${chalk.red(error.message)}`
);
return false;
}
};
// packages/core/src/lib/release/publish.ts
import { exec } from "child_process";
import { promisify } from "util";
import { getPackageManagerCommand } from "@nx/devkit";
import chalk2 from "chalk";
import { releasePublish } from "nx/release";
var publish = async (options) => {
const { otp, dryRun, verbose } = options;
console.log(`${chalk2.magenta.underline("Publish packages")}
`);
try {
const pm = getPackageManagerCommand();
const { stdout } = await promisify(exec)(`${pm.exec} nx run-many -t build`);
console.log(stdout);
const result = await releasePublish({
dryRun,
verbose,
otp
});
const total = Object.values(result).length;
const successful = Object.values(result).filter((r) => r.code === 0).length;
return { successful, total };
} catch (error) {
console.error(`Publish packages: ${chalk2.red(error.message)}`);
return null;
}
};
// packages/core/src/lib/release/release.ts
import {
cancel,
confirm,
group,
intro,
outro,
select,
text
} from "@clack/prompts";
import { readJsonFile as readJsonFile2 } from "@nx/devkit";
import chalk4 from "chalk";
import { releaseVersion } from "nx/release";
import { simpleGit } from "simple-git";
// packages/core/src/lib/utils/whoami.ts
import npmWhoami from "npm-whoami";
async function whoami() {
return new Promise((resolve) => {
npmWhoami((err, user) => {
if (err || !user) {
resolve("");
} else {
resolve(user);
}
});
});
}
// packages/core/src/lib/release/revert-package-json.ts
import { readJsonFile, writeJsonFile } from "@nx/devkit";
import chalk3 from "chalk";
var revertPackageJson = (originPackageFile, projectsVersionData) => {
if (!projectsVersionData["nx-payload"].newVersion) {
return;
}
if (!originPackageFile.devDependencies || !originPackageFile.devDependencies["@cdwr/nx-payload"]) {
return;
}
console.log(
`${chalk3.yellow(`\u26A0\uFE0F Nx auto-update workspace package.json when nx-payload has a new version.
These changes will be reverted to make the update in another context.
`)}`
);
const packageFile = readJsonFile("package.json");
if (packageFile.devDependencies) {
packageFile.version = originPackageFile.version;
packageFile.devDependencies["@cdwr/nx-payload"] = originPackageFile.devDependencies["@cdwr/nx-payload"];
writeJsonFile("package.json", packageFile, { appendNewLine: true });
}
};
// packages/core/src/lib/release/release.ts
var dryRunOutro = () => outro(
`\u{1F453} ${chalk4.green("Done!")} Nothing gets changed when running in ${chalk4.bgYellow(" preview ")} mode`
);
var git = simpleGit();
var release = async () => {
intro(`Let's release some Nx Plugin packages \u{1F4E6}`);
const release2 = await group(
{
mode: () => select({
message: "What parts of the release process do you want to run?",
options: [
{
value: "release",
label: `Default release process \u{1F4AB}`,
hint: "analyze commits, create changelog and publish"
},
{
value: "publish",
label: "Publish a release \u{1F4E6}",
hint: "release must have been pre-generated earlier"
}
],
initialValue: "release"
}),
postponePublish: ({ results: { mode: mode2 } }) => {
if (mode2 === "publish") {
return Promise.resolve("false");
}
return select({
message: "Do you want GitHub Actions to publish the packages to NPM?",
options: [
{
value: "true",
label: "Yes, let GitHub Actions do it",
hint: "recommended"
},
{
value: "false",
label: "No, publish the packages directly"
}
],
initialValue: "true"
});
},
dryRun: () => select({
message: "Do you want to see a preview before making any changes?",
options: [
{
value: "true",
label: `Yes, just a preview \u{1F913}`,
hint: "recommended before the actual run"
},
{
value: "false",
label: "No, run the selected process \u{1F680}"
}
],
initialValue: "true"
}),
otp: ({ results: { dryRun: dryRun2, mode: mode2, postponePublish: postponePublish2 } }) => {
const modeT = mode2;
if (dryRun2 === "false" && (modeT === "publish" || modeT === "release" && postponePublish2 === "false")) {
return text({
message: "Enter NPM OTP code from your 2FA app:",
validate: (value) => {
if (!value) {
return "OTP code is required";
}
if (!/^\d{6}$/.test(value)) {
return "OTP code must be a 6-digit number";
}
return void 0;
}
});
}
return;
},
verbose: () => confirm({
message: "Do you want to enable verbose logging?",
active: "Verbose logging",
inactive: "Normal logging",
initialValue: false
}),
confirmRelease: ({ results: { dryRun: dryRun2 } }) => {
if (dryRun2 === "true") {
return;
}
return confirm({
message: `\u270B You will make changes! Are you sure?`,
active: "Yes, do it!",
inactive: `No, I'm not ready yet`
});
}
},
{
// On Cancel callback that wraps the group
// So if the user cancels one of the prompts in the group this function will be called
onCancel: () => {
cancel("\u{1F6AB} Release cancelled.");
return 0;
}
}
);
const { confirmRelease, verbose } = release2;
const mode = release2.mode;
const dryRun = release2.dryRun === "true";
const otp = Number(release2.otp);
const postponePublish = release2.postponePublish !== "false";
if (confirmRelease === false) {
cancel("\u{1F6AB} Release cancelled.");
return 0;
}
console.log("");
if (otp) {
const npmUser = await whoami();
if (!npmUser) {
console.log(
`${chalk4.red("\u{1F6AB} Npm publish requires you to be logged in.\n")}`
);
console.log(
`You need to authorize using ${chalk4.yellow.bold("npm login")} and follow the instructions.`
);
console.log("After a successful login, try to publish again.");
return 0;
}
}
switch (mode) {
case "publish":
break;
case "release":
{
const originPackageFile = readJsonFile2("package.json");
console.log(`${chalk4.magenta.underline("Analyze changes")}`);
const versionStatus = await releaseVersion({
dryRun,
verbose
});
const projectsVersionData = versionStatus.projectsVersionData;
if (!dryRun) {
revertPackageJson(originPackageFile, projectsVersionData);
await git.add("package.json");
}
if (!await changelogs({
dryRun,
verbose,
versionData: projectsVersionData
})) {
return 1;
}
const newVersionFound = Object.keys(projectsVersionData).some(
(project) => projectsVersionData[project].newVersion
);
if (newVersionFound && postponePublish && !dryRun) {
outro("\u{1F680} The new release will be published by GitHub Actions!");
return 0;
}
if (!newVersionFound) {
return 0;
}
if (dryRun && postponePublish) {
dryRunOutro();
return 0;
}
if (postponePublish) {
return 0;
}
}
break;
}
const publishStats = await publish({ dryRun, otp, verbose });
if (!publishStats) {
return 1;
}
const { successful, total } = publishStats;
if (!total) {
outro("No packages to publish");
return 0;
}
const term = mode === "publish" ? "Publish" : "Release";
const termed = mode === "publish" ? "Published" : "Released";
if (dryRun) {
dryRunOutro();
} else {
outro(
successful === total ? chalk4.green(`\u{1F680} ${termed} successfully!`) : successful > 0 ? chalk4.yellow(
`\u{1F680} ${termed} ${successful} successfully, while ${total - successful} failed`
) : chalk4.red(`\u{1F6AB} ${term} failed`)
);
}
return 0;
};
export {
changelogs,
publish,
release
};