Skip to content

Commit

Permalink
feat: clipanion as cli (#2160)
Browse files Browse the repository at this point in the history
* feat: clipanion as cli

* chore: add version command

* chore: update ts
  • Loading branch information
juanpicado committed Apr 3, 2021
1 parent ba8cba0 commit be2f612
Show file tree
Hide file tree
Showing 22 changed files with 251 additions and 168 deletions.
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"JSONStream": "1.3.5",
"async": "3.2.0",
"body-parser": "1.19.0",
"commander": "7.2.0",
"clipanion": "3.0.0-rc.11",
"compression": "1.7.4",
"cookies": "0.8.0",
"cors": "2.8.5",
Expand Down Expand Up @@ -86,16 +86,16 @@
"@octokit/rest": "16.43.2",
"@types/async": "3.2.4",
"@types/bunyan": "1.8.6",
"@types/express": "4.17.1",
"@types/http-errors": "1.6.3",
"@types/express": "4.17.6",
"@types/http-errors": "1.8.0",
"@types/jest": "26.0.14",
"@types/lodash": "4.14.157",
"@types/lodash": "4.14.167",
"@types/mime": "2.0.1",
"@types/minimatch": "3.0.3",
"@types/node": "12.12.21",
"@types/node": "14.14.37",
"@types/pino": "6.3.6",
"@types/request": "2.48.3",
"@types/semver": "6.2.0",
"@types/request": "2.48.5",
"@types/semver": "7.3.4",
"@typescript-eslint/eslint-plugin": "4.13.0",
"@typescript-eslint/parser": "4.13.0",
"@verdaccio/eslint-config": "^8.5.0",
Expand Down Expand Up @@ -136,7 +136,7 @@
"selfsigned": "1.10.8",
"standard-version": "9.1.1",
"supertest": "6.1.1",
"typescript": "3.9.9",
"typescript": "4.1.3",
"verdaccio-auth-memory": "10.0.0",
"verdaccio-memory": "10.0.0"
},
Expand Down Expand Up @@ -179,7 +179,7 @@
"docker:run": "docker run -it --rm -p 4873:4873 verdaccio/verdaccio:local"
},
"engines": {
"node": ">=8",
"node": ">=12",
"npm": ">=5"
},
"preferGlobal": true,
Expand Down
2 changes: 1 addition & 1 deletion src/api/endpoint/api/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const downloadStream = (
});

stream.on('error', function (err): void {
return res.report_error(err);
return res.locals.report_error(err);
});

res.header(HEADERS.CONTENT_TYPE, HEADERS.OCTET_STREAM);
Expand Down
2 changes: 1 addition & 1 deletion src/api/endpoint/api/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ export function uploadPackageTarball(storage: IStorageHandler) {
});

stream.on('error', function (err) {
return res.report_error(err);
return res.locals.report_error(err);
});

stream.on('success', function () {
Expand Down
4 changes: 2 additions & 2 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,12 @@ const defineAPI = function (config: IConfig, storage: IStorageHandler): any {
if (err.code === 'ECONNABORT' && res.statusCode === HTTP_STATUS.NOT_MODIFIED) {
return next();
}
if (_.isFunction(res.report_error) === false) {
if (_.isFunction(res.locals.report_error) === false) {
// in case of very early error this middleware may not be loaded before error is generated
// fixing that
errorReportingMiddleware(req, res, _.noop);
}
res.report_error(err);
res.locals.report_error(err);
} else {
// Fall to Middleware.final
return next(err);
Expand Down
11 changes: 6 additions & 5 deletions src/api/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export function final(body: FinalBody, req: $RequestExtend, res: $ResponseExtend

if (typeof body === 'object' && _.isNil(body) === false) {
if (typeof (body as MiddlewareError).error === 'string') {
res._verdaccio_error = (body as MiddlewareError).error;
res.locals._verdaccio_error = (body as MiddlewareError).error;
}
body = JSON.stringify(body, undefined, ' ') + '\n';
}
Expand All @@ -210,6 +210,7 @@ export function final(body: FinalBody, req: $RequestExtend, res: $ResponseExtend
// and should just close socket
if (err.message.match(/set headers after they are sent/)) {
if (_.isNil(res.socket) === false) {
// @ts-ignore
res.socket.destroy();
}
return;
Expand Down Expand Up @@ -280,7 +281,7 @@ export function log(config: Config) {
const remoteAddress = req.connection.remoteAddress;
const remoteIP = forwardedFor ? `${forwardedFor} via ${remoteAddress}` : remoteAddress;
let message;
if (res._verdaccio_error) {
if (res.locals._verdaccio_error) {
message = LOG_VERDACCIO_ERROR;
} else {
message = LOG_VERDACCIO_BYTES;
Expand All @@ -298,7 +299,7 @@ export function log(config: Config) {
user: (req.remote_user && req.remote_user.name) || null,
remoteIP,
status: res.statusCode,
error: res._verdaccio_error,
error: res.locals._verdaccio_error,
bytes: {
in: bytesin,
out: bytesout,
Expand Down Expand Up @@ -330,8 +331,8 @@ export function log(config: Config) {

// Middleware
export function errorReportingMiddleware(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
res.report_error =
res.report_error ||
res.locals.report_error =
res.locals.report_error ||
function (err: VerdaccioError): void {
if (err.status && err.status >= HTTP_STATUS.BAD_REQUEST && err.status < 600) {
if (!res.headersSent) {
Expand Down
85 changes: 2 additions & 83 deletions src/lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,96 +3,15 @@
/* eslint no-sync:0 */
/* eslint no-empty:0 */

import path from 'path';
import semver from 'semver';
import { bgYellow, bgRed } from 'kleur';
import { startVerdaccio, listenDefaultCallback } from './bootstrap';
import findConfigFile from './config-path';
import { parseConfigFile } from './utils';

require('pkginfo')(module);

if (process.getuid && process.getuid() === 0) {
global.console.warn(bgYellow().red("*** WARNING: Verdaccio doesn't need superuser privileges. Don't run it under root! ***"));
}

const MIN_NODE_VERSION = '12.0.0';

if (semver.satisfies(process.version, `>=${MIN_NODE_VERSION}`) === false) {
global.console.error(bgRed(`Verdaccio requires at least Node.js ${MIN_NODE_VERSION} or higher, please upgrade your Node.js distribution`));
process.exit(1);
process.emitWarning(`Verdaccio doesn't need superuser privileges. don't run it under root`);
}

process.title = 'verdaccio';

// eslint-disable-next-line import/order
const logger = require('./logger');
logger.setup(null, { logStart: false }); // default setup

const envinfo = require('envinfo');
const commander = require('commander');
const pkgVersion = module.exports.version;
const pkgName = module.exports.name;

commander
.option('-i, --info', 'prints debugging information about the local environment')
.option('-l, --listen <[host:]port>', 'host:port number to listen on (default: localhost:4873)')
.option('-c, --config <config.yaml>', 'use this configuration file (default: ./config.yaml)')
.version(pkgVersion)
.parse(process.argv);

const options = commander.opts();

function init() {
let verdaccioConfiguration;
let configPathLocation;
const cliListener = options.listen;

try {
configPathLocation = findConfigFile(options.config);
verdaccioConfiguration = parseConfigFile(configPathLocation);
process.title = (verdaccioConfiguration.web && verdaccioConfiguration.web.title) || 'verdaccio';

if (!verdaccioConfiguration.self_path) {
verdaccioConfiguration.self_path = path.resolve(configPathLocation);
}
if (!verdaccioConfiguration.https) {
verdaccioConfiguration.https = { enable: false };
}

logger.logger.warn({ file: configPathLocation }, 'config file - @{file}');

startVerdaccio(verdaccioConfiguration, cliListener, configPathLocation, pkgVersion, pkgName, listenDefaultCallback);
} catch (err) {
logger.logger.fatal({ file: configPathLocation, err: err }, 'cannot open config file @{file}: @{!err.message}');
process.exit(1);
}
}

if (options.info) {
// eslint-disable-next-line no-console
console.log('\nEnvironment Info:');
(async () => {
const data = await envinfo.run({
System: ['OS', 'CPU'],
Binaries: ['Node', 'Yarn', 'npm'],
Virtualization: ['Docker'],
Browsers: ['Chrome', 'Edge', 'Firefox', 'Safari'],
npmGlobalPackages: ['verdaccio'],
});
// eslint-disable-next-line no-console
console.log(data);
process.exit(0);
})();
} else if (commander.args.length == 1 && !options.config) {
// handling "verdaccio [config]" case if "-c" is missing in command line
options.config = commander.args.pop();
init();
} else if (commander.args.length !== 0) {
commander.help();
} else {
init();
}
require('./cli/cli');

process.on('uncaughtException', function (err) {
logger.logger.fatal(
Expand Down
32 changes: 32 additions & 0 deletions src/lib/cli/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Cli } from 'clipanion';
import { InfoCommand } from './commands/info';
import { InitCommand } from './commands/init';
import { isVersionValid, MIN_NODE_VERSION } from './utils';
import { VersionCommand } from './commands/version';

require('pkginfo')(module);
const pkgVersion = module.exports.version;

if (process.getuid && process.getuid() === 0) {
process.emitWarning(`Verdaccio doesn't need superuser privileges. don't run it under root`);
}

if (!isVersionValid(process.version)) {
throw new Error(
`Verdaccio requires at least Node.js v${MIN_NODE_VERSION} or higher and you have installed ${process.version},
please upgrade your Node.js distribution`
);
}

const [node, app, ...args] = process.argv;

const cli = new Cli({
binaryLabel: `verdaccio`,
binaryName: `${node} ${app}`,
binaryVersion: pkgVersion,
});

cli.register(InfoCommand);
cli.register(InitCommand);
cli.register(VersionCommand);
cli.runExit(args, Cli.defaultContext);
20 changes: 20 additions & 0 deletions src/lib/cli/commands/info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import envinfo from 'envinfo';
import { Command } from 'clipanion';

export class InfoCommand extends Command {
static paths = [[`--info`], [`-i`]];

async execute() {
this.context.stdout.write('\nEnvironment Info:');
const data = await envinfo.run({
System: ['OS', 'CPU'],
Binaries: ['node', 'yarn', 'npm', 'pnpm'],
Virtualization: ['Docker'],
Browsers: ['Chrome', 'Edge', 'Firefox', 'Safari'],
npmGlobalPackages: ['verdaccio'],
});

this.context.stdout.write(data);
process.exit(0);
}
}
78 changes: 78 additions & 0 deletions src/lib/cli/commands/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import path from 'path';
import { Command, Option } from 'clipanion';

import { startVerdaccio, listenDefaultCallback } from '../../bootstrap';
import findConfigFile from '../../config-path';
import { parseConfigFile } from '../../utils';

require('pkginfo')(module);
const pkgVersion = module.exports.version;
const pkgName = module.exports.name;

export const DEFAULT_PROCESS_NAME: string = 'verdaccio';
const logger = require('../../logger');

export class InitCommand extends Command {
static paths = [Command.Default];

listen = Option.String('-l,--listen', {
description: 'host:port number to listen on (default: localhost:4873)',
});

// eslint-disable-next-line
static usage = Command.Usage({
description: `launch the server`,
details: `
This start the registry in the default port.
When used without arguments, it:
- bootstrap the server at the port \`4873\`
The optional arguments are:
- \`--listen\` to switch the default server port,
- \`--config\` to define a different configuration path location,
`,
examples: [
[`Runs the server with the default configuration`, `verdaccio`],
[`Runs the server in the port 5000`, `verdaccio --listen 5000`],
[
`Runs the server by using a different absolute location of the configuration file`,
`verdaccio --config /home/user/verdaccio/config.yaml`,
],
],
});

config = Option.String('-c,--config', {
description: 'use this configuration file (default: ./config.yaml)',
});

async execute() {
try {
const configPathLocation = findConfigFile(this.config as string);
const verdaccioConfiguration = parseConfigFile(configPathLocation);
if (!verdaccioConfiguration.self_path) {
verdaccioConfiguration.self_path = path.resolve(configPathLocation);
}
if (!verdaccioConfiguration.https) {
verdaccioConfiguration.https = { enable: false };
}

logger.logger.warn({ file: configPathLocation }, 'config file - @{file}');
process.title = (verdaccioConfiguration.web && verdaccioConfiguration.web.title) || 'verdaccio';

startVerdaccio(
verdaccioConfiguration,
this.listen as string,
configPathLocation,
pkgVersion,
pkgName,
listenDefaultCallback
);
} catch (err) {
process.exit(1);
}
}
}
12 changes: 12 additions & 0 deletions src/lib/cli/commands/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Command } from 'clipanion';
require('pkginfo')(module);
const pkgVersion = module.exports.version;

export class VersionCommand extends Command {
static paths = [[`--version`], [`-v`]];

async execute() {
this.context.stdout.write(`v${pkgVersion}`);
process.exit(0);
}
}
13 changes: 8 additions & 5 deletions src/lib/cli/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
/**
* @prettier
* @flow
*/

import path from 'path';
import semver from 'semver';

import { parseAddress } from '../utils';
import { DEFAULT_PORT } from '../constants';
Expand All @@ -14,6 +10,13 @@ export const resolveConfigPath = function (storageLocation: string, file: string
return path.resolve(path.dirname(storageLocation), file);
};

export const MIN_NODE_VERSION = '12';

export function isVersionValid(version) {
return semver.satisfies(version, `>=${MIN_NODE_VERSION}`);
}


/**
* Retrieve all addresses defined in the config file.
* Verdaccio is able to listen multiple ports
Expand Down
Loading

0 comments on commit be2f612

Please sign in to comment.