.github/000077500000000000000000000000001516070074500124235ustar00rootroot00000000000000.github/workflows/000077500000000000000000000000001516070074500144605ustar00rootroot00000000000000.github/workflows/build.yml000066400000000000000000000016761516070074500163140ustar00rootroot00000000000000# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions name: build on: schedule: - cron: '0 0 1 * *' push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: ['lts/*'] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - run: npm i -g grunt-cli - run: npm install - run: grunt .gitignore000066400000000000000000000003301516070074500130470ustar00rootroot00000000000000/build /node_modules /*.log .idea/workspace.xml .idea/tasks.xml .idea/profiles_settings.xml .idea/inspectionProfiles/Project_Default.xml .idea/inspectionProfiles/profiles_settings.xml node_modules/.yarn-integrity .ncurc.json000066400000000000000000000000521516070074500131430ustar00rootroot00000000000000{ "reject": [ "chalk" ] } .npmignore000066400000000000000000000002421516070074500130600ustar00rootroot00000000000000/.idea /artifacts /build /test /node_modules /*.iml /*.ipr /*.iws /.travis.yml /.scrutinizer.yml /Gruntfile.js /*.lock *.log /corifeus-boot.json /.github /.vscodeGruntfile.js000066400000000000000000000004231516070074500133570ustar00rootroot00000000000000module.exports = (grunt) => { const builder = require(`corifeus-builder`); const loader = new builder.loader(grunt); loader.js({ replacer: { type: 'p3x', }, }); grunt.registerTask('default', builder.config.task.build.js); };LICENSE000066400000000000000000000020131516070074500120640ustar00rootroot00000000000000MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.README.md000066400000000000000000000120551516070074500123450ustar00rootroot00000000000000[//]: #@corifeus-header [![NPM](https://img.shields.io/npm/v/p3x-verdaccio-cli.svg)](https://www.npmjs.com/package/p3x-verdaccio-cli) [![Donate for Corifeus / P3X](https://img.shields.io/badge/Donate-Corifeus-003087.svg)](https://paypal.me/patrikx3) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) [![Corifeus @ Facebook](https://img.shields.io/badge/Facebook-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [![Uptime Robot ratio (30 days)](https://img.shields.io/uptimerobot/ratio/m780749701-41bcade28c1ea8154eda7cca.svg)](https://stats.uptimerobot.com/9ggnzcWrw) # 🍶 Verdaccio CLI v2024.4.122 **Bugs are evident™ - MATRIX️** ### NodeJS LTS is supported ### Built on NodeJs version ```txt v22.1.0 ``` # Description [//]: #@corifeus-header:end It is that first `p3x-verdaccio-cli` version that is working with the [Verdaccio](https://github.com/verdaccio). Based on https://github.com/verdaccio/verdaccio/issues/343 Of course, with an option eg. `--storage=azure` it could be enabled. ⚠️ **MAKE SURE TO STOP VERDACCIO BEFORE YOU CONTINUE AND MAKE SURE TO BACKUP BEFORE YOU CHANGE ANYTHING, SO THAT YOU COULD REVERT IF SOMETHING IS NOT STATISFYING!!!** ⚠️ **This is only working with the original Sinopia file system storage driver!!!** **For own packages only works without namespaces!** # Show the help ```bash p3x-verdaccio-cli ``` # Routines ## Cache ### Info ```text p3x-verdaccio-cli --config /var/npm/config.yaml cache info ``` #### Output ```text root@server:~# p3x-verdaccio-cli --config /var/npm/config.yaml cache info Please add a flag --confirm and only add this flag after you stopped Verdaccio! Own packages: corifeus-utils, corifeus-builder, corifeus-builder-angular, corifeus-web, corifeus-web-material, p3x-aes-folder, p3x-angular-compile, p3x-html-pdf, p3x-onenote, p3x-ramdisk, p3x-systemd-manager, p3x-tools, p3x-freenom, grunt-p3x-express, corifeus-server, p3x-redis-ui-server, p3x-redis-ui-material, p3x-redis-ui, p3x-verdaccio-cli, p3x-xml2json Own packages count: 20 Total of package count without own packages: 1668 Please add a flag --confirm and only add this flag after you stopped Verdaccio! ``` ### Clean ```bash # first you can try a dry run and give some output info p3x-verdaccio-cli --config /var/npm/config.yaml --dry cache clean # actually clean the cache p3x-verdaccio-cli --config /var/npm/config.yaml cache clean ``` ## Package remove ```bash # first you can try a dry run and give some output info p3x-verdaccio-cli --config /var/npm/config.yaml --dry pkg-rm my-own-pkg # actually it removes a package p3x-verdaccio-cli --config /var/npm/config.yaml pkg-rm my-own-pkg ``` ## Package ### Keep This function removes old versions from Verdaccio and keep the set minimum versions (eg. keep minimum 3 versions and delete the rest). ```bash # first you can try a dry run and give some output info p3x-verdaccio-cli --config /var/npm/config.yaml --dry pkg keep # actually keeps 3 last versions of your all own packages p3x-verdaccio-cli --config /var/npm/config.yaml pkg keep # if you want more versions, use the --min flag, and keep 10 versions for each packages p3x-verdaccio-cli --config /var/npm/config.yaml pkg keep --min 10 ``` ⚠️ **MAKE SURE TO START VERDACCIO AFTER YOU HAVE DONE!!!** [//]: #@corifeus-footer --- ## Support Our Open-Source Project ❤️ If you appreciate our work, consider starring this repository or making a donation to support server maintenance and ongoing development. Your support means the world to us—thank you! ### Server Availability Our server may occasionally be down, but please be patient. Typically, it will be back online within 15-30 minutes. We appreciate your understanding. ### About My Domains All my domains, including [patrikx3.com](https://patrikx3.com) and [corifeus.com](https://corifeus.com), are developed in my spare time. While you may encounter minor errors, the sites are generally stable and fully functional. ### Versioning Policy **Version Structure:** We follow a Major.Minor.Patch versioning scheme: - **Major:** Corresponds to the current year. - **Minor:** Set as 4 for releases from January to June, and 10 for July to December. - **Patch:** Incremental, updated with each build. **Important Changes:** Any breaking changes are prominently noted in the readme to keep you informed. --- [**P3X-VERDACCIO-CLI**](https://corifeus.com/verdaccio-cli) Build v2024.4.122 [![NPM](https://img.shields.io/npm/v/p3x-verdaccio-cli.svg)](https://www.npmjs.com/package/p3x-verdaccio-cli) [![Donate for Corifeus / P3X](https://img.shields.io/badge/Donate-Corifeus-003087.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [//]: #@corifeus-footer:end bin/000077500000000000000000000000001516070074500116335ustar00rootroot00000000000000bin/p3x-verdaccio-cli.js000077500000000000000000000003441516070074500154110ustar00rootroot00000000000000#!/usr/bin/env node if (!require('fs').existsSync(`${__dirname}/../node_modules`)) { require('child_process').execSync(`cd ${__dirname}/.. && npm install --only=prod`, { stdio: 'inherit' }); } require('../src') package.json000066400000000000000000000023401516070074500133500ustar00rootroot00000000000000{ "name": "p3x-verdaccio-cli", "version": "2024.4.122", "corifeus": { "prefix": "p3x-", "publish": true, "type": "p3x", "code": "Sake", "nodejs": "v22.1.0", "opencollective": false, "reponame": "verdaccio-cli", "build": true }, "bin": { "p3x-verdaccio-cli": "bin/p3x-verdaccio-cli.js" }, "license": "MIT", "description": "🍶 Verdaccio CLI", "main": "src/index.js", "directories": { "test": "test" }, "scripts": { "test": "grunt" }, "repository": { "type": "git", "url": "git+https://github.com/patrikx3/verdaccio-cli.git" }, "keywords": [ "verdaccio", "cli" ], "author": "Patrik Laszlo ", "bugs": { "url": "https://github.com/patrikx3/verdaccio-cli/issues" }, "homepage": "https://corifeus.com/verdaccio-cli", "devDependencies": { "corifeus-builder": "^2024.4.140" }, "dependencies": { "chalk": "^4.1.2", "cli-table": "^0.3.11", "commander": "^12.0.0", "fs-extra": "^11.2.0", "js-yaml": "^4.1.0" }, "engines": { "node": ">=12.13.0" } }src/000077500000000000000000000000001516070074500116525ustar00rootroot00000000000000src/index.js000066400000000000000000000044321516070074500133220ustar00rootroot00000000000000const { program } = require('commander') const lib = require('./lib') const pkg = lib.pkg const start = async () => { program .version(pkg.version) .option('-c, --config ', 'This is required to provide the Verdaccio configuration.') .option('-d, --dry', 'Do not actually clean the cache, just print some info.') program .command('cache ') .description(`The can be info or clean.`) .option('--confirm', 'Only use this flag, when you have stopped Verdaccio at first.') .action(async (routine, options) => { try { await require('./routine/cache').cache(routine, options); } catch (e) { console.error(e) } }) program .command('package') .description(`Please use instead of package => pkg.`) .action(async (options) => { console.info("Please use instead of package => pkg.") }) const pkgKeepMinimum = 3 program .command('pkg ') .description(`The can be info or keep.`) .option('--confirm', 'Only use this flag, when you have stopped Verdaccio at first.') .option('-m, --min ', 'The default and minimum is ' + pkgKeepMinimum, parseInt) .action(async (routine, options) => { try { await require('./routine/package').default({minimum: pkgKeepMinimum}, routine, options); } catch (e) { console.error(e) } }) program .command('pkg-rm ') .description(`The is a name we want to remove.`) .option('--confirm', 'Only use this flag, when you have stopped Verdaccio at first.') .action(async (pkgName, options) => { try { await require('./routine/package').remove(pkgName, options); } catch (e) { console.error(e) } }) program.parse(process.argv); if (!process.argv.slice(2).length) { program.outputHelp(); } else if (program.opts().config === undefined) { return console.error(`The ${pkg.name} program's first argument should be like '--config=/verdaccio/path/config.yaml' and should present.`) } } start(); src/lib.js000066400000000000000000000023401516070074500127550ustar00rootroot00000000000000const pkg = require('../package') const fs = require('fs').promises const path = require('path') const chalk = require('chalk') const parseConfig = async (opts) => { const yaml = require('js-yaml') //console.warn(opts) const yamlString = (await fs.readFile(opts.config)).toString() const yamlConfig = yaml.load(yamlString) //console.warn(yamlConfig) return yamlConfig } module.exports.defaults = async () => { const { program } = require('commander') //console.warn(options) const configPath = path.dirname(program.opts().config) const config = await parseConfig({ config: program.opts().config }) const storagePath = path.resolve(configPath, config.storage) const dbPath = path.resolve(storagePath, '.verdaccio-db.json'); const db = require(dbPath) return { configPath: configPath, config: config, storagePath: storagePath, db: db, dbPath: dbPath, } } module.exports.confirmInfo = (options) => { if (options.confirm !== true) { console.info(chalk.yellow(` Please add a flag --confirm and only add this flag after you stopped Verdaccio! `)) } } module.exports.parseConfig = parseConfig module.exports.pkg = pkg src/routine/000077500000000000000000000000001516070074500133375ustar00rootroot00000000000000src/routine/cache.js000066400000000000000000000044651516070074500147510ustar00rootroot00000000000000const fs = require('fs').promises const path = require('path') const fsExtra = require('fs-extra') const lib = require('../lib') const availableCacheRoutine = [ 'clean', 'info', ] const chalk = require('chalk') const cache = async (routine, options) => { const { program } = require('commander') if (!availableCacheRoutine.includes(routine)) { return console.error(`The cache is not available '${routine}', the available routines are ${availableCacheRoutine.join(', ')}.`) } const defaults = await lib.defaults(); const packageFolders = await fs.readdir(defaults.storagePath) //console.warn(packageFolders) const packageFoldersWithoutOwn = [] const packageFolderPathStatAwaitable = [] for (let packageFolderPath of packageFolders) { packageFolderPathStatAwaitable.push( fs.stat(path.resolve(defaults.storagePath, packageFolderPath)) ) } const packageFolderPathStatAwaitableResult = await Promise.all(packageFolderPathStatAwaitable) for (let statIndex in packageFolderPathStatAwaitableResult) { const stat = packageFolderPathStatAwaitableResult[statIndex] //console.warn(stat) const packagePath = packageFolders[statIndex] if (stat.isDirectory() && !defaults.db.list.includes(packagePath)) { packageFoldersWithoutOwn.push(packagePath) } } if (options.confirm !== true) { console.info('Please add a flag --confirm and only add this flag after you stopped Verdaccio!') } if (program.opts().dry === true || routine === 'info' || options.confirm !== true) { console.info(` Own packages: ${defaults.db.list.join(', ')} Own packages count: ${defaults.db.list.length} Total of package count without own packages: ${packageFoldersWithoutOwn.length} `) lib.confirmInfo(options) return } const removablePathAwaitable = [] for (let packagePath of packageFoldersWithoutOwn) { removablePathAwaitable.push( fsExtra.remove(path.resolve(defaults.storagePath, packagePath)) ) } await Promise.all(removablePathAwaitable) console.info('Removed all cached packages and kept all own packages.') //console.warn(packageFoldersWithoutOwn.length, packageFolders.length) } module.exports.cache = cache src/routine/package.js000066400000000000000000000153521516070074500152760ustar00rootroot00000000000000const fs = require('fs').promises const fsExtra = require('fs-extra') const path = require('path') const lib = require('../lib') const availablePackageRoutine = [ 'keep', ] const Table = require('cli-table'); const chalk = require('chalk') const defaultFn = async (settings, routine, options) => { const {root} = settings; const { program } = require('commander') if (!availablePackageRoutine.includes(routine)) { return console.error(`The '${routine}' is not available , the available routines are ${availablePackageRoutine.join(', ')}.`) } const {minimum} = settings if (options.min === undefined) { options.min = minimum } if (isNaN(options.min)) { console.warn(`The options --min variable be a number.`) return } if (options.min < minimum) { console.warn(`The options --min variable number must be at least ${minimum}.`) return } const defaults = await lib.defaults(); const db = defaults.db const awaitablePackages = [] for (let dbItem of db.list) { awaitablePackages.push( fs.readFile(path.resolve(defaults.storagePath, `${dbItem}/package.json`)) ) } let foundBuffers try { foundBuffers = await Promise.all(awaitablePackages); } catch (e) { if (e.code === 'ENOENT') { const pkgName = path.basename(path.dirname(e.path)) console.warn(chalk.yellow(` It looks like the package '${pkgName}' present is the db, but is deleted from file system. To fix this error, please do this: ${program._name} --config ${program.opts().config} pkg-rm ${pkgName} `)) return } else { throw e } } const pkgRegistry = {} for (let dbItemIndex in db.list) { const dbItem = db.list[dbItemIndex] const pkgBuffer = foundBuffers[dbItemIndex] pkgRegistry[dbItem] = JSON.parse(pkgBuffer.toString()); } if (program.opts().dry === true || routine === 'info' || options.confirm !== true) { console.info(` Own packages: ${defaults.db.list.join(', ')} Own packages count: ${defaults.db.list.length} Will keep last versions: ${options.min} `) const table = new Table({ head: ['Package', 'Version count'] }); for (let pkgName of Object.keys(pkgRegistry)) { table.push( [pkgName, Object.keys(pkgRegistry[pkgName].time).length - 2] ) } console.info(table.toString()) lib.confirmInfo(options) return } const table = new Table({ head: ['Package', 'Kept versions'] }); for await(let pkgName of Object.keys(pkgRegistry)) { const pkgNamed = pkgRegistry[pkgName] if (!pkgNamed.hasOwnProperty('_distfiles')) { pkgNamed['__distfiles'] = {} } if (!pkgNamed.hasOwnProperty('_attachments')) { pkgNamed['_attachments'] = {} } const versions = Object.assign({}, pkgNamed.time) delete versions['created']; delete versions['modified'] Object.keys(versions).forEach(versionKey => { versions[versionKey] = new Date(versions[versionKey]) }) // the sorted shows the order array => by time let sortedVersions = {} sortedVersions = Object.keys(versions).sort((a, b) => { return versions[b] - versions[a] }) const createVersionFile = (version) => { return `${pkgName}/${pkgName}-${version}.tgz` } let minBuffer = 0 for await(let version of sortedVersions) { minBuffer++ if (minBuffer <= options.min) { continue } /* pkg.versions = version as key pkg.time = versions as key, but there is modified and created pkg._distfiles => delete key pkg._distfiles[pkg-name-verions.tgz] pkg._attachments => delete key pkg._attachments[pkg-name-versions.tgz] */ const versionName = createVersionFile(version) delete pkgNamed.versions[version] delete pkgNamed.time[version] delete pkgNamed._distfiles[versionName] delete pkgNamed._attachments[versionName] await fsExtra.remove(path.resolve(defaults.storagePath, versionName)) } for await (let pkgVersionKey of Object.keys(pkgNamed.versions)) { if (!pkgNamed.time.hasOwnProperty(pkgVersionKey)) { delete pkgNamed.versions[pkgVersionKey] const versionName = createVersionFile(pkgVersionKey) delete pkgNamed._distfiles[pkgVersionKey] delete pkgNamed._attachments[pkgVersionKey] await fsExtra.remove(path.resolve(defaults.storagePath, versionName)) } } const removeFileNamed = async (prop) => { let versionsNamed = Object.keys(pkgNamed[prop]) for await(let versionName of versionsNamed) { let version = versionName.slice(pkgName.length + 1) version = version.slice(0, -4) if (!pkgNamed.time.hasOwnProperty(version)) { delete pkgNamed.versions[version] delete pkgNamed._distfiles[versionName] delete pkgNamed._attachments[versionName] await fsExtra.remove(path.resolve(defaults.storagePath, versionName)) } } } await removeFileNamed('_distfiles') await removeFileNamed('_attachments') table.push( [pkgName, `${Object.keys(pkgNamed.time).length - 2}`] ) //console.info(sortedVersions) //console.log(Object.keys(sortedVersions).length) //console.info() await fs.writeFile(path.resolve(defaults.storagePath, `${pkgName}/package.json`), JSON.stringify(pkgNamed, null, 4)) } console.info(table.toString()) } const remove = async (pkgName, options) => { const { program } = require('commander') const defaults = await lib.defaults(); const db = defaults.db if (!db.list.includes(pkgName)) { console.warn(` This package '${pkgName}' is not present in the db! The available db packages: ${db.list.join(", ")} `) return } if (program.opts().dry === true || options.confirm !== true) { console.info(`Could remove package: ${pkgName}`) lib.confirmInfo(options) return; } await fsExtra.remove(path.resolve(defaults.storagePath, pkgName)) db.list = db.list.filter(dbItem => dbItem !== pkgName) await fs.writeFile(defaults.dbPath, JSON.stringify(db)) console.info(`Removed package: ${pkgName}`) } module.exports.default = defaultFn module.exports.remove = remove test/000077500000000000000000000000001516070074500120425ustar00rootroot00000000000000test/cli/000077500000000000000000000000001516070074500126115ustar00rootroot00000000000000test/cli/cache-clean000077500000000000000000000001651516070074500146640ustar00rootroot00000000000000#!/usr/bin/env bash ./bin/p3x-verdaccio-cli.js --config=/home/patrikx3/Desktop/Downloads/npm/config.yaml cache clean test/cli/cache-clean-dry000077500000000000000000000001711516070074500154550ustar00rootroot00000000000000#!/usr/bin/env bash ./bin/p3x-verdaccio-cli.js --config=/home/patrikx3/Desktop/Downloads/npm/config.yaml -d cache clean