UNPKG

@feflow/cli

Version:
235 lines (208 loc) 7.07 kB
import fs from 'fs'; import path from 'path'; import osenv from 'osenv'; import semver from 'semver'; import createLogger from '../logger'; import { UniversalPkg } from '../universal-pkg/dep/pkg'; import { loadPlugin } from '../plugin/load-universal-plugin'; import { updateCli } from '../native/upgrade'; import { getPkgInfo, updateUniversalPlugin } from '../native/install'; import LockFile from '../../shared/lock-file'; import { install } from '../../shared/npm'; import { FEFLOW_BIN, FEFLOW_LIB, FEFLOW_ROOT, UNIVERSAL_MODULES, UNIVERSAL_PKG_JSON, UPDATE_COLLECTION, UPDATE_KEY, UPDATE_LOCK, } from '../../shared/constant'; import { setServerUrl } from '../../shared/git'; import { parseYaml } from '../../shared/yaml'; import pkgJson from '../../../package.json'; import { PluginUpdateMsg, UpdateData, UniversalPluginUpdateMsg } from './'; import { getInstalledPlugins, getLatestVersion, getUniversalPluginVersion, promisify, updatePluginsVersion, } from './utils'; import { FeflowConfig, isValidConfig } from '../../shared/type-predicates'; import Feflow from '../index'; // 设置特殊的进程名字 process.title = 'feflow-update-process'; const { cacheValidate, debug, silent, latestVersion } = process.env; const logger = createLogger({ name: 'feflow-update-process', debug: Boolean(debug), silent: Boolean(silent), }); const root = path.join(osenv.home(), FEFLOW_ROOT); const rootPkg = path.join(root, 'package.json'); const configPath = path.join(root, '.feflowrc.yml'); const universalPkgPath = path.join(root, UNIVERSAL_PKG_JSON); const universalModules = path.join(root, UNIVERSAL_MODULES); const config = parseYaml(configPath); const bin = path.join(root, FEFLOW_BIN); const lib = path.join(root, FEFLOW_LIB); const dbFile = path.join(root, UPDATE_COLLECTION); const updateLock = path.join(root, UPDATE_LOCK); const universalPkg = new UniversalPkg(universalPkgPath); const updateFile = new LockFile(dbFile, updateLock, logger); if (!isValidConfig(config)) { process.exit(); } const { packageManager } = config; // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const ctx = { root, universalPkg, logger, universalModules, bin, lib, config, } as Feflow; process.on('uncaughtException', (e) => { logger.error(`update_exception: ${e.name}: ${e.message} => ${e.stack}`); }); process.on('unhandledRejection', handleRejection); let updateData: UpdateData; update(); async function update() { updateFile .read(UPDATE_KEY) .then(async (data) => { updateData = data && typeof data === 'object' ? data : {}; return Promise.all([startUpdateCli(), checkPluginsUpdate(), checkUniversalPluginsUpdate()]).catch((error) => { handleRejection(error); }); }) .then(() => { updateData.update_lock = undefined; updateFile.update(UPDATE_KEY, updateData); }) .catch((reason) => { updateData.update_lock = undefined; updateFile.update(UPDATE_KEY, updateData); logger.debug(reason); handleRejection(reason); }); } function handleRejection(reason: unknown) { logger.error(`update_rejection: ${reason}`); } async function startUpdateCli() { if (!latestVersion) { return; } await updateCli(packageManager); updateData.cli_update_msg = { version: pkgJson.version, latestVersion, }; updateData.latest_cli_version = ''; } async function startPluginsUpdate(plugins: PluginUpdateMsg[]) { updatePluginsVersion(rootPkg, plugins); const needUpdatePlugins: string[] = []; plugins.forEach((plugin) => { needUpdatePlugins.push(plugin.name); }); return install(packageManager, root, packageManager === 'yarn' ? 'add' : 'install', needUpdatePlugins, false).then(() => { updateData.plugins_update_msg = plugins; updateData.latest_plugins = undefined; logger.info('Plugin update success'); }); } async function checkPluginsUpdate() { return new Promise<void>((resolve, reject) => { (async () => { try { if (Boolean(cacheValidate)) { // 用缓存数据 const updatePkg = updateData.latest_plugins; if (updatePkg?.length) { await startPluginsUpdate(updatePkg); } resolve(); } else { const installedPlugins = getInstalledPlugins(); const pluginsToUpdate: PluginUpdateMsg[] = []; const checkUpdatePromises = installedPlugins.map(async (pluginName: string) => { const pluginPath = path.join(root, 'node_modules', pluginName, 'package.json'); const content = fs.readFileSync(pluginPath, { encoding: 'utf8', }); const pkgInfo = JSON.parse(content); const localVersion = pkgInfo.version; const latestVersion = await getLatestVersion(pluginName, packageManager); if (latestVersion && semver.gt(latestVersion, localVersion)) { pluginsToUpdate.push({ name: pluginName, latestVersion, }); } logger.debug('All plugins is in latest version'); }); // 实时拉取最新更新 await Promise.all(checkUpdatePromises); if (pluginsToUpdate.length) { await startPluginsUpdate(pluginsToUpdate); } resolve(); } } catch (e) { logger.error(e); reject(e); } })(); }); } async function checkUniversalPluginsUpdate() { let updatePkg = []; const { serverUrl } = config as FeflowConfig; if (!serverUrl) { return; } setServerUrl(serverUrl); const { latest_universal_plugins: latestUniversalPlugins } = updateData; if (String(cacheValidate) === 'true' && latestUniversalPlugins) { // 用缓存数据 updatePkg = latestUniversalPlugins; } else { // 实时拉取最新更新 for (const [pkg, version] of universalPkg.getInstalled()) { // 记录更改项 const pkgInfo = await getPkgInfo(ctx, `${pkg}@${version}`).catch((e: string) => { logger.error(`update_error => pkg: ${pkg}@${version} => error: ${e}`); }); if (!pkgInfo) { continue; } const versionObj = await getUniversalPluginVersion(pkgInfo, universalPkg); if (versionObj.latestVersion) { updatePkg.push(versionObj); } } } if (updatePkg.length) { const updateTasks = updatePkg.map(async (item: UniversalPluginUpdateMsg) => { const { name, installVersion } = item; // 使用之前的方法进行更新,后续修改 const plugin = loadPlugin(ctx, name, installVersion); return promisify(updateUniversalPlugin, ctx, name, installVersion, plugin); }); // 顺序执行多语言插件的更新来保证依赖的插件不会同时更新而冲突 for (const updateTask of updateTasks) { await ( await updateTask )(); } updateData.universal_plugins_update_msg = updatePkg; updateData.latest_universal_plugins = undefined; } }