From 918c6e2ee57b205192ca84fabff5092be5b4d4ab Mon Sep 17 00:00:00 2001 From: elrrrrrrr Date: Wed, 25 Oct 2023 15:04:55 +0800 Subject: [PATCH] feat: force clean (#41) --- integration/index.2.test.js | 2 +- integration/index.test.js | 4 +++- integration/workspaces.test.js | 11 ++++++++-- packages/cli/bin/rapid.js | 20 ++++++++++++------ packages/cli/lib/index.js | 30 +++++++++++++-------------- packages/cli/lib/nydusd/fuse_mode.js | 25 +++++++++++----------- packages/cli/lib/nydusd/index.js | 4 ++-- packages/cli/lib/nydusd/nydusd_api.js | 10 ++++++++- packages/cli/lib/util.js | 14 +++++++++++++ 9 files changed, 79 insertions(+), 41 deletions(-) diff --git a/integration/index.2.test.js b/integration/index.2.test.js index c942046..ca48b14 100644 --- a/integration/index.2.test.js +++ b/integration/index.2.test.js @@ -19,7 +19,7 @@ describe('test/index.v2.test.js', () => { let cwd; afterEach(async () => { - await clean(cwd); + await clean({ cwd }); if (process.platform === 'darwin') { try { await forceExitDaemon(); diff --git a/integration/index.test.js b/integration/index.test.js index 908a652..4e800e7 100644 --- a/integration/index.test.js +++ b/integration/index.test.js @@ -16,7 +16,9 @@ const { describe('test/tnpm-install-rapid.test.js', () => { let fixture; afterEach(async () => { - await clean(fixture); + await clean({ + cwd: fixture, + }); if (process.platform === 'darwin') { await forceExitDaemon(); } else { diff --git a/integration/workspaces.test.js b/integration/workspaces.test.js index 72b841f..8bb2fb1 100644 --- a/integration/workspaces.test.js +++ b/integration/workspaces.test.js @@ -17,8 +17,12 @@ describe('test/workspaces.test.js', () => { it('should install lodash successfully', async () => { cwd = path.join(__dirname, './fixtures/workspaces'); - await clean(cwd); + await clean({ + cwd, + force: true, + }); await install({ + nydusMode: 'FUSE', cwd, pkg: require(path.join(cwd, 'package.json')), depsTreePath: path.join(cwd, 'package-lock.json'), @@ -34,7 +38,10 @@ describe('test/workspaces.test.js', () => { assert(lodash1.version.startsWith('1.')); assert(lodash2.version.startsWith('2.')); } finally { - await clean(cwd); + await clean({ + cwd, + force: true, + }); if (process.platform === 'darwin') { await forceExitDaemon(); } else { diff --git a/packages/cli/bin/rapid.js b/packages/cli/bin/rapid.js index f732a6b..ef2748c 100755 --- a/packages/cli/bin/rapid.js +++ b/packages/cli/bin/rapid.js @@ -4,12 +4,14 @@ const { clean, install, list } = require('../lib/index.js'); const yargs = require('yargs'); -const { NpmFsMode } = require('../lib/constants.js'); +const { NpmFsMode, NYDUS_TYPE } = require('../lib/constants.js'); const util = require('../lib/util'); +const path = require('node:path'); yargs .command({ command: 'install', + aliases: [ 'i', 'ii' ], describe: 'Install dependencies', builder: yargs => { return yargs @@ -30,10 +32,12 @@ yargs const pkgRes = await util.readPkgJSON(); const pkg = pkgRes?.pkg || {}; + await util.shouldFuseSupport(); await install({ cwd, pkg, mode, + nydusMode: NYDUS_TYPE.FUSE, ignoreScripts, }); @@ -43,17 +47,21 @@ yargs }, }) .command({ - command: 'clean', + command: 'clean [path]', + aliases: [ 'c', 'unmount', 'uninstall' ], describe: 'Clean up the project', - handler: async () => { - const cwd = process.cwd(); - await clean(cwd); - + handler: async argv => { + let cwd = argv.path || process.cwd(); + if (cwd.endsWith('node_modules') || cwd.endsWith('node_modules/')) { + cwd = path.dirname(cwd); + } + await clean({ nydusMode: NYDUS_TYPE.FUSE, cwd, force: false }); console.log('[rapid] clean finished'); }, }) .command({ command: 'list', + aliases: 'l', describe: 'List rapid mount info', handler: async () => { const cwd = process.cwd(); diff --git a/packages/cli/lib/index.js b/packages/cli/lib/index.js index b0997df..aff3bd0 100644 --- a/packages/cli/lib/index.js +++ b/packages/cli/lib/index.js @@ -18,11 +18,6 @@ const { MirrorConfig } = require('binary-mirror-config'); // 有依赖树(package-lock.json)走 npm / npminstall 极速安装 exports.install = async options => { options.env = util.getEnv(options.env, options.args); - const nydusMode = await nydusd.getNydusMode(); - if (!nydusMode || nydusMode === NYDUS_TYPE.NATIVE) { - await util.shouldFuseSupport(); - } - const { packageLock } = options.packageLock || (await util.readPackageLock(options.cwd)); const currentMountInfo = await util.listMountInfo(); @@ -35,8 +30,13 @@ exports.install = async options => { const mountedInfo = currentMountInfo.find(item => item.mountPoint === nodeModulesDir); if (mountedInfo) { - console.log(`[rapid] ${nodeModulesDir} already mounted, try to clean`); - await exports.clean(path.join(options.cwd, pkgPath), true); + console.time(`[rapid] ${nodeModulesDir} already mounted, try to clean`); + await exports.clean({ + nydusMode: options.nydusMode, + cwd: options.cwd, + force: true, + }); + console.timeEnd(`[rapid] ${nodeModulesDir} already mounted, try to clean`); } await fs.mkdir(baseDir, { recursive: true }); @@ -56,7 +56,7 @@ exports.install = async options => { await downloadDependency.download(options); assert(Object.keys(packageLock).length, '[rapid] depsJSON invalid.'); - await nydusd.startNydusFs(nydusMode, options.cwd, options.pkg); + await nydusd.startNydusFs(options.nydusMode, options.cwd, options.pkg); console.time('[rapid] wait for access'); await util.ensureAccess(options.cwd, packageLock); @@ -67,20 +67,20 @@ exports.install = async options => { console.timeEnd('[rapid] run lifecycle scripts'); }; -exports.clean = async function clean(cwd) { - const mode = await nydusd.getNydusInstallMode(cwd); - if (!mode) { - console.log(`[rapid] invalid mode ${mode} ignore`); +exports.clean = async function clean({ nydusMode = NYDUS_TYPE.FUSE, cwd, force }) { + const listInfo = await util.listMountInfo(); + if (!listInfo.length) { + console.log('[rapid] no mount info found.'); + return; } const { pkg } = await util.readPkgJSON(cwd); - await nydusd.endNydusFs(mode, cwd, pkg); + await nydusd.endNydusFs(nydusMode, cwd, pkg, force); }; exports.list = async () => { const running = await nydusdApi.isDaemonRunning(); if (!running) { - console.error('[rapid] nydusd is not running, please run `rapid install` first.'); - return; + console.warn('[rapid] nydusd is not running, please run `rapid install` first.'); } const listInfo = await util.listMountInfo(); if (!listInfo.length) { diff --git a/packages/cli/lib/nydusd/fuse_mode.js b/packages/cli/lib/nydusd/fuse_mode.js index 72a46ac..7812285 100644 --- a/packages/cli/lib/nydusd/fuse_mode.js +++ b/packages/cli/lib/nydusd/fuse_mode.js @@ -13,6 +13,7 @@ const { wrapSudo, getWorkdir, getAllPkgPaths, + safeExeca, } = require('../util'); const nydusdApi = require('./nydusd_api'); @@ -98,23 +99,21 @@ ${nodeModulesDir}`; } } -async function endNydusFs(cwd, pkg) { +async function endNydusFs(cwd, pkg, force = false) { const allPkgs = await getAllPkgPaths(cwd, pkg); + const umountCmd = force ? 'umount -f' : 'umount'; for (const pkgPath of allPkgs) { - const { - dirname, - overlay, - baseDir, - nodeModulesDir, - } = await getWorkdir(cwd, pkgPath); + const { dirname, overlay, baseDir, nodeModulesDir } = await getWorkdir( + cwd, + pkgPath + ); if (os.type() === 'Darwin') { - console.log(`[rapid] umount ${nodeModulesDir}`); - await execa.command(`umount ${nodeModulesDir}`); - // hdiutil detach - await execa.command(`hdiutil detach ${overlay}`); + console.log(`[rapid] ${umountCmd} ${nodeModulesDir}`); + await safeExeca(`umount ${nodeModulesDir}`, force ? `umount -f ${nodeModulesDir}` : ''); + await safeExeca(`hdiutil detach ${overlay}`, force ? `hdiutil detach -force ${overlay}` : ''); } else { - await execa.command(wrapSudo(`umount ${nodeModulesDir}`)); - await execa.command(wrapSudo(`umount ${overlay}`)); + await execa.command(wrapSudo(`${umountCmd} ${nodeModulesDir}`)); + await execa.command(wrapSudo(`${umountCmd} ${overlay}`)); } await nydusdApi.umount(`/${dirname}`); // 清除 nydus 相关目录 diff --git a/packages/cli/lib/nydusd/index.js b/packages/cli/lib/nydusd/index.js index c66e4ac..70f22e6 100644 --- a/packages/cli/lib/nydusd/index.js +++ b/packages/cli/lib/nydusd/index.js @@ -37,14 +37,14 @@ exports.startNydusFs = async function(mode, cwd, pkg) { await impl.start(cwd, pkg); }; -exports.endNydusFs = async function(mode, cwd, pkg) { +exports.endNydusFs = async function(mode, cwd, pkg, force) { if (!mode || mode === NYDUS_TYPE.NATIVE) { console.log('[rapid] nydusd is not running, skip clean'); return; } const impl = fsImplMap[mode]; assert(impl, `can not find fs impl for mode: ${mode}`); - await impl.end(cwd, pkg); + await impl.end(cwd, pkg, force); }; exports.getNydusMode = async function(cwd) { diff --git a/packages/cli/lib/nydusd/nydusd_api.js b/packages/cli/lib/nydusd/nydusd_api.js index 8dcfa01..3a652bd 100644 --- a/packages/cli/lib/nydusd/nydusd_api.js +++ b/packages/cli/lib/nydusd/nydusd_api.js @@ -159,7 +159,15 @@ async function forceExitDaemon() { await execa.command('killall -9 nydusd'); } catch (e) { // ignore, nydusd quits with error, but it's ok - e.message = 'exit nydusd faield: ' + e.message; + e.message = 'umount nydusd mnt failed: ' + e.message; + console.warn(e); + } + + try { + await execa.command('killall -9 nydusd'); + } catch (e) { + // ignore, nydusd quits with error, but it's ok + e.message = 'exit nydusd failed: ' + e.message; console.warn(e); } } diff --git a/packages/cli/lib/util.js b/packages/cli/lib/util.js index 5913e62..fe499af 100644 --- a/packages/cli/lib/util.js +++ b/packages/cli/lib/util.js @@ -54,6 +54,19 @@ function wrapSudo(shScript) { return `sudo ${shScript}`; } +async function safeExeca(command, fallback) { + try { + await execa.command(command); + } catch (e) { + console.warn(`[rapid] ${command} error: `, e); + try { + fallback && (await execa.command(fallback)); + } catch (e) { + // ignore + } + } +} + // 需要手动写入,保证 path 路径符合预期 async function createNydusdConfigFile(path) { await fs.writeFile(path, JSON.stringify({ @@ -617,3 +630,4 @@ exports.isFlattenPackage = isFlattenPackage; exports.resolveBinMap = resolveBinMap; exports.getFileEntryMode = getFileEntryMode; exports.getEnv = getEnv; +exports.safeExeca = safeExeca;