UNPKG

@roboplay/sage

Version:
321 lines (319 loc) 11.8 kB
import { findPackagePath } from 'robo.js/dist/cli/utils/utils.js'; import { Command } from 'commander'; import { logger } from '../core/logger.js'; import { getPackageManager, checkSageUpdates, checkUpdates, IS_WINDOWS, exec } from '../core/utils.js'; import { loadConfig } from 'robo.js/dist/core/config.js'; import { color, composeColors } from '../core/color.js'; import fs from 'node:fs'; import path from 'node:path'; import { Separator, checkbox, select } from '@inquirer/prompts'; import { readFile } from 'node:fs/promises'; import { Flashcore } from 'robo.js'; const command = new Command("upgrade").description("Upgrades your Robo to the latest version").option("-y --yes", "installs updates without showing changelogs").option("-f --force", "forcefully install").option("-ns --no-self-check", "do not check for updates to Sage CLI").option("-s --silent", "do not print anything").option("-v --verbose", "print more information for debugging").action(upgradeAction); var upgrade_default = command; async function upgradeAction(options) { logger({ enabled: !options.silent, level: options.verbose ? "debug" : "info" }).info(`Checking for updates...`); logger.debug(`CLI Options:`, options); logger.debug(`Package manager:`, getPackageManager()); logger.debug(`Current working directory:`, process.cwd()); if (options.selfCheck) { await checkSageUpdates(options.yes); } const config = await loadConfig(); await Flashcore.$init({ keyvOptions: config.flashcore?.keyv }); const plugins = config.plugins; plugins.push(["robo.js", {}]); const packageJsonPath = path.join(process.cwd(), await findPackagePath("robo.js", process.cwd()), "package.json"); logger.debug(`Package JSON path:`, packageJsonPath); const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8")); logger.debug(`Package JSON:`, packageJson); const update = await checkUpdates(packageJson, config, true); logger.debug(`Update payload:`, update); const autoAccept = options.yes; await updateRobo(plugins, config, autoAccept); } async function getChangelog(url) { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.text(); let currentChangelog = null; let currentChangeType = null; for (const line of data.split("\n")) { const versionMatch = line.match(/^## (\d+\.\d+\.\d+)/); if (versionMatch) { if (currentChangelog) break; currentChangelog = { version: versionMatch[1], patch: [], major: [], minor: [] }; continue; } const changeTypeMatch = line.match(/^### (Patch|Minor|Major) Changes/); if (changeTypeMatch) { currentChangeType = changeTypeMatch[1].toLowerCase(); continue; } if (currentChangelog && currentChangeType) { const changeMatch = line.match(/^- [a-f0-9]+: (.+)/); if (changeMatch) { currentChangelog[currentChangeType].push(changeMatch[1]); } } } return currentChangelog; } catch (error) { logger.error("Failed to fetch and process the changelog", error); return null; } } function printChangelog(changelog) { logger.log(composeColors(color.bold, color.blue, color.underline)(`Version: ${changelog.version}`)); if (changelog.major.length > 0) { logger.log(composeColors(color.red, color.bold)("Major Changes:")); changelog.major.forEach((change) => logger.log(`- ${change}`)); } if (changelog.minor.length > 0) { logger.log(composeColors(color.bold, color.yellow)("Minor Changes:")); changelog.minor.forEach((change) => logger.log(`- ${change}`)); } if (changelog.patch.length > 0) { logger.log(composeColors(color.bold, color.green)("Patch Changes:")); changelog.patch.forEach((change) => logger.log(`- ${change}`)); } logger.log("\n"); } let _id = 0; const CHANGES = { configDirectory: { id: ++_id, name: "Config directory", description: "The config directory has been renamed from `.config` to `config`." } }; async function check(name, version) { logger.info(`Checking version ${version}...`); const breaking = []; const suggestions = []; if (name !== "robo.js") { logger.debug(`No changes to check for`, name); return { breaking, suggestions }; } if (fs.existsSync(path.join(process.cwd(), ".config"))) { breaking.push(CHANGES.configDirectory); } return { breaking, suggestions }; } async function execute(changes) { logger.info(`Applying changes:`, changes.map((change) => change.name).join(", ")); for (const change of changes) { if (change.id === CHANGES.configDirectory.id) { logger.debug(`Renaming config directory...`); fs.renameSync(path.join(process.cwd(), ".config"), path.join(process.cwd(), "config")); } } logger.info(`Successfully applied changes!`); } const CustomSeparator = "----- \u{1F389} -----"; async function updateRobo(plugins, config, autoAccept) { const u_options = []; const hasUpdate = []; for (const plugin of plugins) { const plugingName = IS_WINDOWS ? plugin[0].replaceAll("\\", "/") : plugin[0]; const packagePath = await findPackagePath(plugingName, process.cwd()); logger.debug("Checking updates for", color.bold(plugingName), "at path", color.bold(packagePath)); const packageJsonPath = path.join(packagePath, "package.json"); const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8")); const update = await checkUpdates(packageJson, config, true); logger.debug(`Update payload for ${plugingName}:`, update); const pluginOnRegistry = await fetch(`https://registry.npmjs.org/${packageJson.name}/latest`); if (!pluginOnRegistry.ok) { logger.info( composeColors( color.yellowBright, color.bold )(`Skipping ${plugingName}, not found on registry, probably local plugin!`) ); continue; } if (!update.hasUpdate) { logger.info(composeColors(color.green, color.bold)(`${plugingName} is up to date!`)); continue; } if (autoAccept && update.hasUpdate) { hasUpdate.push({ data: { name: plugingName, extra: { ...update, name: "" } } }); continue; } const upgradeOptions = [ { name: plugingName, value: JSON.stringify({ data: { name: plugingName, extra: update } }), short: "pl" } ]; if (update.changelogUrl) { upgradeOptions.splice(1, 0, { name: "Read changelog", value: JSON.stringify({ ...update, name: plugingName }), short: "cl" }); hasUpdate.push( JSON.stringify({ ...update, name: plugingName }) ); } u_options.push(new Separator(plugingName)); u_options.push(...upgradeOptions); logger.info( composeColors(color.green, color.bold)(`A new version of ${plugingName} is available!`), color.dim(`(v${update.currentVersion} -> v${update.latestVersion})`) ); logger.log(""); } if (autoAccept) { await upgradeSelectedPlugins(hasUpdate); logger.info(composeColors(color.green, color.bold)(`Your Robo is up to date!`)); return; } if (u_options.length > 0) { await showListOfPlugins(u_options, hasUpdate); } else { logger.info(composeColors(color.green, color.bold)(`Your Robo is up to date!`)); } } async function showListOfPlugins(u_options, hasUpdate) { const selectedPlugins = await checkbox( { message: "Select plugins that you want to update:", choices: u_options.filter((option) => option instanceof Separator === false && option.short !== "cl"), loop: false }, { clearPromptOnDone: false } ); if (selectedPlugins.length > 0) { u_options.push(new Separator(CustomSeparator)); u_options.push({ name: "Proceed update", value: "update" }); u_options.push({ name: "Cancel", value: "abort" }); await showChangelogList(selectedPlugins, u_options, hasUpdate); } } async function showChangelogList(pluginData, u_options, hasUpdate) { console.clear(); const pluginNames = pluginData.map((plugin) => { const parsed = JSON.parse(plugin); if (isValidPlugin(parsed)) { return parsed.data.name; } }); const selectedChangelog = await select( { message: "See the change logs for the plug-ins you selected or proceed with the upgrade", choices: u_options.filter((option) => { if (option instanceof Separator) { if (option.separator === CustomSeparator) { return option; } if (pluginNames.includes(option.separator)) { return { ...option, separate: option.separator + ":" }; } } if (option instanceof Separator === false) { if (option.value === "abort" || option.value === "update") { return option; } if (option.short === "cl") { const value = JSON.parse(option.value); if (pluginNames.includes(value.name)) { return option; } } } }), loop: false }, { clearPromptOnDone: false } ); if (typeof selectedChangelog === "string" && hasUpdate.includes(selectedChangelog)) { const JSONParseChangeLog = JSON.parse(selectedChangelog); const changelog = await getChangelog(JSONParseChangeLog.changelogUrl); printChangelog(changelog); const upgrade = await select( { message: ``, choices: [{ name: "back", value: false }], loop: false }, { clearPromptOnDone: true } ); logger.log(""); if (!upgrade) { await showChangelogList(pluginData, u_options, hasUpdate); return; } } if (selectedChangelog === "update") { if (Array.isArray(pluginData)) { const map = pluginData.map((plugin) => { if (typeof plugin === "string") { const parsed = JSON.parse(plugin); if (isValidPlugin(parsed)) return parsed; } }); await upgradeSelectedPlugins(map); return; } logger.error("An error happened while treating the data..."); return; } if (selectedChangelog === "abort") { logger.info("Aborting plugin upgrade!"); return; } } async function upgradeSelectedPlugins(selectedPlugins, autoAccept) { const packageManager = getPackageManager(); const command2 = packageManager === "npm" ? "install" : "add"; logger.debug(`Package manager:`, packageManager); const pluginStringFromArray = selectedPlugins.map((plugin) => `${plugin.data.name}@${plugin.data.extra.latestVersion}`).join(" "); await exec(`${packageManager} ${command2} ${pluginStringFromArray}`); for (const plugin of selectedPlugins) { const { extra, name } = plugin.data; const data = await check(name, extra.latestVersion); logger.debug(`Check data:`, data); if (data.breaking.length > 0 || data.suggestions.length > 0 && !autoAccept) { const changes = await checkbox({ message: "Which changes would you like to apply?", choices: [ ...data.breaking.map((change) => ({ name: change.name, value: change })), new Separator(), ...data.suggestions.map((change) => ({ name: change.name, value: change })) ] }); logger.log(""); await execute(changes); } logger.ready(`Successfully upgraded ${name} to v${extra.latestVersion}! \u{1F389}`); } } function isValidPlugin(plugin) { if (plugin?.data !== void 0) { return true; } } export { upgrade_default as default }; //# sourceMappingURL=out.js.map //# sourceMappingURL=upgrade.js.map