From fa88553fe67ed53594a36b0e1035f3f6b76c3c5c Mon Sep 17 00:00:00 2001 From: elrrrrrrr Date: Fri, 20 Oct 2023 00:58:51 -0500 Subject: [PATCH] feat: mac rapid (#34) * feat: mac rapid * fix: ci --- integration/index.2.test.js | 35 +++-- package.json | 1 - packages/cli/bin/rapid.js | 127 ++++++++---------- packages/cli/lib/dep/arborist_logger.js | 37 ----- packages/cli/lib/dep/dep_context.js | 42 ------ packages/cli/lib/dep/index.js | 87 ------------ packages/cli/lib/dep/local_cache_resolver.js | 29 ---- packages/cli/lib/dep/local_resolver.js | 57 -------- packages/cli/lib/dep/remote_cache_resolver.js | 17 --- packages/cli/lib/dep/remote_resolver.js | 17 --- packages/cli/lib/download_dependency.js | 1 + packages/cli/lib/downloader.js | 1 + packages/cli/lib/index.js | 55 +++++--- packages/cli/lib/nydusd/fuse_mode.js | 1 + packages/cli/lib/nydusd/index.js | 5 +- packages/cli/lib/nydusd/nydusd_api.js | 15 +++ packages/cli/lib/util.js | 31 ++++- packages/cli/package.json | 1 + packages/cli/test/dep.test.js | 47 ------- packages/cli/test/install.test.js | 45 ------- 20 files changed, 166 insertions(+), 485 deletions(-) delete mode 100644 packages/cli/lib/dep/arborist_logger.js delete mode 100644 packages/cli/lib/dep/dep_context.js delete mode 100644 packages/cli/lib/dep/index.js delete mode 100644 packages/cli/lib/dep/local_cache_resolver.js delete mode 100644 packages/cli/lib/dep/local_resolver.js delete mode 100644 packages/cli/lib/dep/remote_cache_resolver.js delete mode 100644 packages/cli/lib/dep/remote_resolver.js delete mode 100644 packages/cli/test/dep.test.js delete mode 100644 packages/cli/test/install.test.js diff --git a/integration/index.2.test.js b/integration/index.2.test.js index eb1b248..c942046 100644 --- a/integration/index.2.test.js +++ b/integration/index.2.test.js @@ -21,7 +21,11 @@ describe('test/index.v2.test.js', () => { afterEach(async () => { await clean(cwd); if (process.platform === 'darwin') { - await forceExitDaemon(); + try { + await forceExitDaemon(); + } catch (err) { + console.warn('force exit daemon error: %s', err.message); + } } else { await exitDaemon(); } @@ -43,7 +47,7 @@ describe('test/index.v2.test.js', () => { if (process.platform === 'linux') { it('should run postinstall successfully', async () => { cwd = path.join(__dirname, './fixtures/node-crc'); - await coffee.fork(rapid, [ '--by=npm', `--deps-tree-path=${path.join(cwd, 'package-lock.json')}` ], { cwd }) + await coffee.fork(rapid, [ 'install', '--by=npm', `--deps-tree-path=${path.join(cwd, 'package-lock.json')}` ], { cwd }) .debug() .expect('stdout', /execute 1 lifecycle script\(s\)/) .expect('code', 0) @@ -56,7 +60,7 @@ describe('test/index.v2.test.js', () => { describe('INIT_CWD', async () => { it('should set INIT_CWD', async () => { cwd = path.join(__dirname, './fixtures/init-cwd'); - await coffee.fork(rapid, [ '--by=npminstall', `--deps-tree-path=${path.join(cwd, 'package-lock.json')}` ], { cwd }) + await coffee.fork(rapid, [ 'install', '--by=npminstall', `--deps-tree-path=${path.join(cwd, 'package-lock.json')}` ], { cwd }) .debug() .expect('stdout', new RegExp(`INIT_CWD=${process.cwd()}`)) .expect('code', 0) @@ -67,8 +71,17 @@ describe('test/index.v2.test.js', () => { describe('production mode', async () => { it('should install production deps', async () => { cwd = path.join(__dirname, './fixtures/prod-deps'); - await coffee.fork(rapid, [ - '--by=npminstall', '--production', `--deps-tree-path=${path.join(cwd, 'package-lock.json')}` ], { cwd }) + await coffee + .fork( + rapid, + [ + 'install', + '--by=npminstall', + '--production', + `--deps-tree-path=${path.join(cwd, 'package-lock.json')}`, + ], + { cwd } + ) .debug() .expect('code', 0) .end(); @@ -81,7 +94,7 @@ describe('test/index.v2.test.js', () => { it('should install node-canvas successfully', async () => { cwd = path.join(__dirname, './fixtures/canvas'); await coffee - .fork(rapid, [ `--deps-tree-path=${path.join(cwd, 'package-lock.json')}` ], { cwd }) + .fork(rapid, [ 'install' ], { cwd }) .debug() .expect('code', 0) .end(); @@ -100,8 +113,9 @@ describe('test/index.v2.test.js', () => { delete process.env[k]; } } + await coffee - .fork(rapid, [ `--deps-tree-path=${path.join(cwd, 'package-lock.json')}` ], { + .fork(rapid, [ 'install', '--ignore-scripts' ], { cwd, }) .debug() @@ -118,8 +132,8 @@ describe('test/index.v2.test.js', () => { cwd = path.join(__dirname, './fixtures/esbuild'); await coffee .fork(rapid, [ - '--by=npm', - `--deps-tree-path=${path.join(cwd, 'package-lock.json')}`, + 'install', + '--ignore-scripts', ], { cwd, }) @@ -137,8 +151,9 @@ describe('test/index.v2.test.js', () => { cwd = path.join(__dirname, './fixtures/esbuild'); await coffee .fork(rapid, [ + 'install', '--by=npminstall', - `--deps-tree-path=${path.join(cwd, 'package-lock.json')}`, + '--ignore-scripts', ], { cwd, }) diff --git a/package.json b/package.json index 18d0347..0f8e0b4 100644 --- a/package.json +++ b/package.json @@ -54,4 +54,3 @@ "node": ">=14.19.1" } } - diff --git a/packages/cli/bin/rapid.js b/packages/cli/bin/rapid.js index 881c8cb..f732a6b 100755 --- a/packages/cli/bin/rapid.js +++ b/packages/cli/bin/rapid.js @@ -2,84 +2,63 @@ 'use strict'; - -const { clean, install } = require('../lib/index.js'); -const parser = require('yargs-parser'); +const { clean, install, list } = require('../lib/index.js'); +const yargs = require('yargs'); const { NpmFsMode } = require('../lib/constants.js'); -const httpclient = require('../lib/httpclient'); const util = require('../lib/util'); -async function runner() { - const params = parser(process.argv.slice(2), { - alias: { - 'save-dev': [ 'D' ], - save: [ 'save-prod', 'S', 'P' ], - 'save-optional': [ 'O' ], - 'save-bundle': [ 'B' ], - 'save-exact': [ 'E' ], - version: [ 'v' ], - usage: [ 'H', 'h', 'help', '?' ], - global: [ 'g' ], - detail: [ 'd' ], - mode: [ 'by' ], - prefix: [ 'C' ], - registry: [ 'reg' ], +yargs + .command({ + command: 'install', + describe: 'Install dependencies', + builder: yargs => { + return yargs + .option('ignore-scripts', { + describe: 'Skip running scripts during install', + type: 'boolean', + }) + .option('by', { + describe: 'Set the installation mode, support npm or npminstall', + type: 'string', + }); }, - boolean: [ - 'clean', - 'save', - 'save-dev', - 'save-optional', - 'save-bundle', - 'save-peer', - 'save-exact', - 'version', - 'usage', - 'global', - 'detail', - 'update', - 'package-lock-only', - 'ignore-scripts', - 'experimental-local-resolver', - 'legacy-peer-deps', - 'force', - 'strict-peer-deps', - 'update-lockfile', - ], - string: [ - 'registry', - 'prefix', - 'deps-tree-path', - 'lock-id', - 'mode', - 'omit', - 'cache-dir', - ], - }); + handler: async argv => { + const ignoreScripts = argv['ignore-scripts']; + const mode = argv.by || NpmFsMode.NPM; + + const cwd = process.cwd(); + const pkgRes = await util.readPkgJSON(); + const pkg = pkgRes?.pkg || {}; - const cwd = params.prefix || process.cwd(); - if (params.clean) { - await clean(cwd); - } else { - const pkgRes = await util.readPkgJSON(); - const pkg = pkgRes?.pkg || {}; - await install({ - ...params, - cwd, - pkg, - registry: params.registry || 'https://registry.npmjs.org', - mode: params.mode || NpmFsMode.NPM, - env: process.env, - httpclient, - }); - } -} + await install({ + cwd, + pkg, + mode, + ignoreScripts, + }); + + console.log('[rapid] install finished'); + // 首次执行 nydusd 后台服务可能会 hang 住输入 + process.exit(0); + }, + }) + .command({ + command: 'clean', + describe: 'Clean up the project', + handler: async () => { + const cwd = process.cwd(); + await clean(cwd); -runner() - .then(() => { - process.exit(0); + console.log('[rapid] clean finished'); + }, + }) + .command({ + command: 'list', + describe: 'List rapid mount info', + handler: async () => { + const cwd = process.cwd(); + await list(cwd); + }, }) - .catch(e => { - console.error(e); - process.exit(1); - }); + .help() + .parse(); diff --git a/packages/cli/lib/dep/arborist_logger.js b/packages/cli/lib/dep/arborist_logger.js deleted file mode 100644 index 09044c4..0000000 --- a/packages/cli/lib/dep/arborist_logger.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -class ArboristLogger { - silly() { - // this.write(..._, 'SILLY'); - } - - http(...args) { - this.write(...args, 'HTTP'); - } - - error(...args) { - this.write(...args, 'ERROR'); - } - - warn(...args) { - this.write(...args, 'WARN'); - } - - info(...args) { - this.write(...args, 'INFO'); - } - - debug(...args) { - this.write(...args, 'DEBUG'); - } - - log(...args) { - this.write(...args, 'LOG'); - } - - write(...args) { - console.log(...args); - } -} - -module.exports = ArboristLogger; diff --git a/packages/cli/lib/dep/dep_context.js b/packages/cli/lib/dep/dep_context.js deleted file mode 100644 index 847dc73..0000000 --- a/packages/cli/lib/dep/dep_context.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict'; - -/** - * 依赖计算上下文 - */ -class DepContext { - /** - * @param {object} options - - * @param {string} options.cwd - 当前目录 - * @param {string} options.pkg - 当前项目 package.json - * @param {string} options.lockId - 指定 lockId 来获取依赖树 - * @param {string} options.depsTreePath - 本地依赖树缓存路径 - * @param {object} options.update: 对比 package.json 是否有变化,更新依赖树 - * @param {boolean} options.all: 更新全部依赖,默认 false - * @param {Array} options.names: 更新指定依赖 - * @param {Array} options.deps 安装新依赖 - * @param {string} options.registry: 指定 registry - * @param {boolean} options.updateLockfile: 更新依赖树 - */ - constructor(options) { - this.cwd = options.cwd; - this.lockId = options.lockId; - this.depsTreePath = options.depsTreePath; - this.pkg = options.pkg; - this.update = options.update || { - all: false, - names: options.names?.length > 0 ? options.names : [], - }; - - this.arboristOptions = { - registry: options.registry || 'https://registry.npmjs.org', - force: options.force, - legacyPeerDeps: options.legacyPeerDeps, - strictPeerDeps: options.strictPeerDeps, - }; - - this.modifyDeps = options.modifyDeps; - this.updateLockfile = options.updateLockfile; - } -} - -module.exports = DepContext; diff --git a/packages/cli/lib/dep/index.js b/packages/cli/lib/dep/index.js deleted file mode 100644 index de4fc65..0000000 --- a/packages/cli/lib/dep/index.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict'; - -const DepContext = require('./dep_context'); -const RemoteCacheResolver = require('./remote_cache_resolver'); -const RemoteResolver = require('./remote_resolver'); -const LocalResolver = require('./local_resolver'); -const LocalCacheResolver = require('./local_cache_resolver'); - -// 依赖树解析优先级: -// 0. 指定的 lockId -// 1. 指定的本地依赖 -// 2. 服务端生成依赖 -class DepResolver { - /** - * @param {object} options - - * @param {string} options.cwd - 当前目录 - * @param {string} [options.cacheDir] - pacote cacheDir - * @param {string} [options.registry] - pacote registry - * @param {string} [options.experimentalLocalResolver] - 实验性本地依赖解析器 - * @param {string} [options.downloader] - LocalResolver 预下载用 - * @param {string} options.pkg - 当前项目 package.json - * @param {string} options.lockId - 指定 lockId 来获取依赖树 - * @param {string} options.depsTreePath - 本地依赖树缓存路径 - * @param {object} options.update: 对比 package.json 是否有变化,更新依赖树 - * @param {boolean} options.all: 更新全部依赖,默认 false - * @param {Array} options.names: 更新指定依赖 - */ - constructor(options) { - this.options = options; - this.ctx = new DepContext(options); - this.RemoteCacheResolver = options.RemoteCacheResolver || RemoteCacheResolver; - this.RemoteResolver = options.RemoteResolver || RemoteResolver; - this.LocalResolver = options.LocalResolver || LocalResolver; - this.LocalCacheResolver = options.LocalCacheResolver || LocalCacheResolver; - } - - async resolve() { - if (this.ctx.lockId) { - // 本地安装时,直接通过 npm 执行安装和更新依赖树逻辑 - if (this.options.experimentalLocalResolver) { - console.log('[rapid] use local resolver to update deps tree'); - const resolver = new this.LocalResolver(this.ctx, this.options); - return resolver.resolve(); - } - // rapid i lodash.has --lockId=xx 或者 node_modules/.lock-id.txt 存在 - // 在对应 lockId 的基础上进行依赖树的更新 - if (this.ctx.modifyDeps || this.ctx.updateLockfile) { - console.log('[rapid] use remote resolver to update deps tree'); - const resolver = new this.RemoteResolver(this.ctx, this.options); - return resolver.resolve(); - } - // 根据 lockId 去读取服务端的依赖树 - try { - console.log(`[rapid] use remote cache resolver to resolve lockId: ${this.ctx.lockId}`); - const resolver = new this.RemoteCacheResolver(this.ctx, this.options); - return await resolver.resolve(); - } catch (e) { - e.message = `resolve with lockId: ${this.ctx.lockId} failed: ` + e.message; - console.warn(e); - } - } - // 本地依赖树缓存存在 直接读取本地文件 - if (this.ctx.depsTreePath) { - try { - console.log(`[rapid] use local cache resolver to resolve deps tree: ${this.ctx.depsTreePath}`); - const resolver = new this.LocalCacheResolver(this.ctx, this.options); - return await resolver.resolve(); - } catch (e) { - e.message = `resolve with package-lock.json: ${this.ctx.depsTreePath} failed: ` + e.message; - console.warn(e); - } - } - let resolver; - if (this.options.experimentalLocalResolver) { - // 在本地通过 http 接口生成依赖树 - console.log('[rapid] use local resolver to generate deps tree'); - resolver = new this.LocalResolver(this.ctx, this.options); - } else { - // 在服务端生成依赖树 - console.log('[rapid] use remote resolver to generate deps tree'); - resolver = new this.RemoteResolver(this.ctx, this.options); - } - return await resolver.resolve(); - } -} - -module.exports = DepResolver; diff --git a/packages/cli/lib/dep/local_cache_resolver.js b/packages/cli/lib/dep/local_cache_resolver.js deleted file mode 100644 index 26ce95e..0000000 --- a/packages/cli/lib/dep/local_cache_resolver.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -const fs = require('node:fs/promises'); - -class LocalCacheResolver { - /** - * @param {DepContext} ctx - - * @param {Object} options - - */ - constructor(ctx, options) { - this.depsTreePath = ctx.depsTreePath; - this.httpclient = options.httpclient; - } - - async resolve() { - try { - console.time('[rapid] resolve local deps tree'); - const depTreeString = await fs.readFile(this.depsTreePath, 'utf8'); - const depsTree = JSON.parse(depTreeString); - console.timeEnd('[rapid] resolve local deps tree'); - return depsTree; - } catch (e) { - e.message = `read dep tree from ${this.depsTreePath} failed: ${e.message}`; - throw e; - } - } -} - -module.exports = LocalCacheResolver; diff --git a/packages/cli/lib/dep/local_resolver.js b/packages/cli/lib/dep/local_resolver.js deleted file mode 100644 index 25b68c3..0000000 --- a/packages/cli/lib/dep/local_resolver.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict'; - -const Arborist = require('@npmcli/arborist'); -const ArboristLogger = require('./arborist_logger'); -const PACKAGE_SERVICE = Symbol.for('packageService'); -const ROOT_PACKAGE = Symbol.for('rootPackage'); - -class LocalResolver { - constructor(ctx, options) { - const { registry, force, legacyPeerDeps, strictPeerDeps } = options; - this.ctx = ctx; - this.options = options; - this.installOptions = { - registry, - force, - legacyPeerDeps, - strictPeerDeps, - }; - } - - async resolve() { - console.time('[rapid] generate deps tree'); - const pkgLockJson = await this.generatePackageLockJson(this.ctx.pkg); - console.timeEnd('[rapid] generate deps tree'); - return pkgLockJson; - } - - async generatePackageLockJson(pkgJson) { - const { registry, force, legacyPeerDeps, strictPeerDeps } = this.options; - const arboristOptions = { - nodeVersion: pkgJson.engines && pkgJson.engines.node, - npmVersion: pkgJson.engines && pkgJson.engines.npm, - // arborist 仍然会读取 cwd 下的 pkg.json 进行依赖计算 - path: this.ctx.cwd, - cache: this.options.cacheDir, - log: new ArboristLogger(), - update: this.options.update, - strictSSL: false, - lockfileVersion: 3, - force, - registry, - legacyPeerDeps, - strictPeerDeps, - ...this.options.arboristOptions, - [PACKAGE_SERVICE]: this.packageService, - [ROOT_PACKAGE]: pkgJson, - }; - - const arborist = new Arborist(arboristOptions); - const idealTree = await arborist.buildIdealTree({}); - const meta = idealTree.meta; - const res = meta.commit(); - return res; - } -} - -module.exports = LocalResolver; diff --git a/packages/cli/lib/dep/remote_cache_resolver.js b/packages/cli/lib/dep/remote_cache_resolver.js deleted file mode 100644 index 13aa56e..0000000 --- a/packages/cli/lib/dep/remote_cache_resolver.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -class RemoteCacheResolver { - /** - * @param {DepContext} ctx - - */ - constructor(ctx) { - this.ctx = ctx; - this.lockId = ctx.lockId; - } - - async resolve() { - throw new Error('remote cache resolver not implemented.'); - } -} - -module.exports = RemoteCacheResolver; diff --git a/packages/cli/lib/dep/remote_resolver.js b/packages/cli/lib/dep/remote_resolver.js deleted file mode 100644 index 3da06d4..0000000 --- a/packages/cli/lib/dep/remote_resolver.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -class RemoteResolver { - /** - * @param {DepContext} ctx - - */ - constructor(ctx) { - this.ctx = ctx; - this.pkg = ctx.pkg; - } - - async resolve() { - throw new Error('remote resolver not implemented.'); - } -} - -module.exports = RemoteResolver; diff --git a/packages/cli/lib/download_dependency.js b/packages/cli/lib/download_dependency.js index 0b5bc60..e417c22 100644 --- a/packages/cli/lib/download_dependency.js +++ b/packages/cli/lib/download_dependency.js @@ -57,6 +57,7 @@ async function download(options) { console.time('[rapid] parallel download time'); await downloader.download(depsTree); + console.log('[rapid] download finished'); const { tocMap, indices } = downloader.dumpdata; await downloader.shutdown(); diff --git a/packages/cli/lib/downloader.js b/packages/cli/lib/downloader.js index e6ca735..281cb9b 100644 --- a/packages/cli/lib/downloader.js +++ b/packages/cli/lib/downloader.js @@ -58,6 +58,7 @@ class Downloader { async download(pkgLockJson) { const tasks = this.createDownloadTask(pkgLockJson); + console.log('[rapid] downloads %d packages', tasks.length); await this.rapidDownloader.batchDownloads(tasks); } diff --git a/packages/cli/lib/index.js b/packages/cli/lib/index.js index 03f7582..97e6074 100644 --- a/packages/cli/lib/index.js +++ b/packages/cli/lib/index.js @@ -5,7 +5,6 @@ const assert = require('node:assert'); const path = require('node:path'); const downloadDependency = require('./download_dependency'); const Scripts = require('./scripts').Scripts; -const DepResolver = require('./dep'); const { nydusdConfigFile, tarBucketsDir, @@ -13,33 +12,37 @@ const { } = require('./constants'); const util = require('./util'); const nydusd = require('./nydusd'); +const nydusdApi = require('./nydusd/nydusd_api'); const { MirrorConfig } = require('binary-mirror-config'); // 有依赖树(package-lock.json)走 npm / npminstall 极速安装 exports.install = async options => { - if (options.update) { - await exports.clean(options.cwd); - } - // set args to npm_config_xx env options.env = util.getEnv(options.env, options.args); const nydusMode = await nydusd.getNydusMode(); if (!nydusMode || nydusMode === NYDUS_TYPE.NATIVE) { await util.shouldFuseSupport(); } - const resolver = new DepResolver(options); - const depsJSON = await resolver.resolve(); - if (options.packageLockOnly) { - await util.saveLockFile(options.cwd, depsJSON, resolver.ctx.lockId); - return; - } + const { packageLock } = options.packageLock || (await util.readPackageLock(options.cwd)); + + const currentMountInfo = await util.listMountInfo(); const allPkgs = await util.getAllPkgPaths(options.cwd, options.pkg); - await Promise.all(allPkgs.map(pkgPath => async () => { - const { baseDir, tarIndex } = await util.getWorkdir(options.cwd, pkgPath); + + await Promise.all(allPkgs.map(async pkgPath => { + const { baseDir, tarIndex, nodeModulesDir } = await util.getWorkdir(options.cwd, pkgPath); + + 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)); + } + await fs.mkdir(baseDir, { recursive: true }); await fs.mkdir(path.dirname(tarIndex), { recursive: true }); })); + await fs.mkdir(tarBucketsDir, { recursive: true }); await util.createNydusdConfigFile(nydusdConfigFile); const mirrorConfig = new MirrorConfig({ @@ -49,24 +52,38 @@ exports.install = async options => { mirrorConfig.setEnvs(options); options.scripts = new Scripts(options); - options.depsTree = depsJSON; + options.depsTree = packageLock; await downloadDependency.download(options); - assert(Object.keys(depsJSON).length, '[rapid] depsJSON invalid.'); + assert(Object.keys(packageLock).length, '[rapid] depsJSON invalid.'); await nydusd.startNydusFs(nydusMode, options.cwd, options.pkg); // 执行 lifecycle scripts console.time('[rapid] run lifecycle scripts'); await options.scripts.runLifecycleScripts(mirrorConfig); console.timeEnd('[rapid] run lifecycle scripts'); - // 写入依赖树缓存 - const { depsJSONPath } = await util.getWorkdir(options.cwd); - await fs.writeFile(depsJSONPath, JSON.stringify(depsJSON), 'utf8'); }; exports.clean = async function clean(cwd) { const mode = await nydusd.getNydusInstallMode(cwd); - if (!mode) return; + if (!mode) { + console.log(`[rapid] invalid mode ${mode} ignore`); + } const { pkg } = await util.readPkgJSON(cwd); await nydusd.endNydusFs(mode, cwd, pkg); }; + +exports.list = async () => { + const running = await nydusdApi.isDaemonRunning(); + if (!running) { + console.error('[rapid] nydusd is not running, please run `rapid install` first.'); + return; + } + const listInfo = await util.listMountInfo(); + if (!listInfo.length) { + console.log('[rapid] no mount info found.'); + return; + } + // 不展示 overlay 信息 + console.table(listInfo.filter(_ => _.mountPoint.endsWith('node_modules'))); +}; diff --git a/packages/cli/lib/nydusd/fuse_mode.js b/packages/cli/lib/nydusd/fuse_mode.js index 8c374cf..d6cf036 100644 --- a/packages/cli/lib/nydusd/fuse_mode.js +++ b/packages/cli/lib/nydusd/fuse_mode.js @@ -106,6 +106,7 @@ async function endNydusFs(cwd, pkg) { 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}`); diff --git a/packages/cli/lib/nydusd/index.js b/packages/cli/lib/nydusd/index.js index 8b6d06b..c66e4ac 100644 --- a/packages/cli/lib/nydusd/index.js +++ b/packages/cli/lib/nydusd/index.js @@ -38,7 +38,10 @@ exports.startNydusFs = async function(mode, cwd, pkg) { }; exports.endNydusFs = async function(mode, cwd, pkg) { - if (mode === NYDUS_TYPE.NATIVE) return; + 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); diff --git a/packages/cli/lib/nydusd/nydusd_api.js b/packages/cli/lib/nydusd/nydusd_api.js index 95e0b65..8dcfa01 100644 --- a/packages/cli/lib/nydusd/nydusd_api.js +++ b/packages/cli/lib/nydusd/nydusd_api.js @@ -206,9 +206,24 @@ async function umount(mountpoint) { debug('umount result: %j', result); } +// 查询当前挂载信息 +async function list() { + const result = await urllib.request(`${daemonUrl}`, { + method: 'GET', + socketPath, + dataType: 'json', + }); + + if (result.status === 200 && result.data.state === 'RUNNING') { + return Object.values(result.data.backend_collection); + } +} + exports.mount = mount; exports.remount = remount; exports.umount = umount; exports.initDaemon = initDaemon; exports.exitDaemon = exitDaemon; exports.forceExitDaemon = forceExitDaemon; +exports.isDaemonRunning = isDaemonRunning; +exports.list = list; diff --git a/packages/cli/lib/util.js b/packages/cli/lib/util.js index 23e2f6d..dbbc397 100644 --- a/packages/cli/lib/util.js +++ b/packages/cli/lib/util.js @@ -92,9 +92,8 @@ async function shouldFuseSupport() { if (os.type() === 'Darwin') { try { await fs.stat('/usr/local/bin/go-nfsv4'); - console.warn('[rapid] rapid mode is supported on macOS **experimentally**.'); } catch (error) { - throw new NotSupportedError('install macFUSE first.'); + throw new NotSupportedError('install fuse-t first.'); } } @@ -534,6 +533,34 @@ exports.readPkgJSON = async function readPkgJSON(cwd) { return { pkg, pkgPath }; }; +exports.readPackageLock = async function readPackageLock(cwd) { + const lockPath = path.join(cwd || exports.findLocalPrefix(), './package-lock.json'); + const packageLock = JSON.parse(await fs.readFile(lockPath, 'utf8')); + return { packageLock, lockPath }; +}; + +// 列出当前 mount 的 fuse endpoint +// 目前只支持 fuse-t +exports.listMountInfo = async function listMountInfo() { + + const { stdout } = await execa('mount'); + // 拆分输出为每行 + const mountLines = stdout.split('\n'); + + return mountLines.filter(_ => { + if (_.includes(nydusdMnt)) { + return false; + } + return _.includes(baseRapidModeDir()) || _.startsWith('fuse'); + }).map(line => { + const parts = line.split(' '); + const device = parts[0]; + const mountPoint = parts[2]; + // const options = parts.slice(3).join(' '); + return { device, mountPoint }; + }).sort((a, b) => a.device.localeCompare(b.device)); +}; + exports.getWorkdir = getWorkdir; exports.validDep = validDep; exports.getDisplayName = getDisplayName; diff --git a/packages/cli/package.json b/packages/cli/package.json index eef08b0..43d26e9 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -26,6 +26,7 @@ "runscript": "^1.5.3", "semver": "^7.3.8", "urllib": "^3.16.1", + "yargs": "^17.7.2", "yargs-parser": "^9.0.2" }, "homepage": "https://github.com/cnpm/rapid", diff --git a/packages/cli/test/dep.test.js b/packages/cli/test/dep.test.js deleted file mode 100644 index 11daaeb..0000000 --- a/packages/cli/test/dep.test.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict'; - -const assert = require('node:assert'); -const path = require('node:path'); -const httpclient = require('../lib/httpclient'); -const DepResolver = require('../lib/dep'); - -describe('test/dep.test.js', () => { - describe('local cache resolve', () => { - it('should read load cache', async () => { - const resolver = new DepResolver({ - httpclient, - cwd: '/dev/null', - pkg: {}, - lockId: null, - depsTreePath: path.join(__dirname, 'fixtures/rapid-mode-download-local-deps/tree.json'), - update: { - all: false, - names: [], - }, - }); - await resolver.resolve(); - }); - }); - - describe('local resolve', () => { - const cwd = path.join(__dirname, 'fixtures/local_resolver'); - it('should generate true', async () => { - const resolver = new DepResolver({ - httpclient, - cwd, - pkg: require(path.join(cwd, 'package.json')), - lockId: null, - depsTreePath: null, - update: { - all: false, - names: [], - }, - experimentalLocalResolver: true, - }); - const lock = await resolver.resolve(); - assert(lock); - assert(lock.packages['node_modules/lodash.get']); - assert(lock.packages['node_modules/lodash.set']); - }); - }); -}); diff --git a/packages/cli/test/install.test.js b/packages/cli/test/install.test.js deleted file mode 100644 index b930d41..0000000 --- a/packages/cli/test/install.test.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -const fs = require('node:fs/promises'); -const path = require('node:path'); -const os = require('node:os'); -const assert = require('node:assert'); -const mm = require('mm'); -const { install } = require('../lib/index'); - -describe('install', () => { - let cwd; - beforeEach(async () => { - mm(os, 'type', () => 'Linux'); - cwd = await fs.mkdtemp('/tmp/'); - await fs.writeFile(`${cwd}/package.json`, JSON.stringify({ - name: 'test', - version: '1.0.0', - dependencies: { - 'lodash.has': '4.5.2', - }, - devDependencies: { - 'lodash.has': '4.5.2', - }, - })); - }); - afterEach(async () => { - mm.restore(); - await fs.rm(cwd, { recursive: true, force: true }); - }); - - it('should generate tree successfully', async () => { - await install({ - cwd, - pkg: require(path.join(cwd, 'package.json')), - experimentalLocalResolver: true, - env: process.env, - httpclient: require('../lib/httpclient'), - packageLockOnly: true, - }); - - const lockfile = require(path.join(cwd, 'package-lock.json')); - assert.strictEqual(lockfile.lockfileVersion, 3); - assert(Object.keys(lockfile.packages), 2); - }); -});