UNPKG

@cdwr/core

Version:

A set of core utilities for the Codeware ecosystem.

299 lines (293 loc) 9.03 kB
// 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 };