0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-12-16 21:56:25 -05:00

feat: run server init as promise (#3210)

* feat: run server init as promise

* chore: format

* fix: format

* fix: format

* fix: restore files

* fix: restore files

* fix: disable steps

* fix: init log on cli

* fix: init log on cli

* fix: init log on cli

* fix: init log on cli

* fix: init log on cli

* fix: init log on cli

* fix: init log on cli

* fix: init log on cli

* fix: init log on cli

* fix: init log on cli

* fix: init log on cli
This commit is contained in:
Juan Picado 2022-06-02 17:39:49 +02:00 committed by GitHub
parent 4f59bb8f20
commit 42194c7302
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 314 additions and 39 deletions

View file

@ -8,7 +8,7 @@ on:
name: 'E2E Gatsby.js CLI with verdaccio'
jobs:
npm6:
name: 'npm:gatsby example'
name: 'npm6:gatsby example'
runs-on: ubuntu-latest
steps:
@ -32,7 +32,7 @@ jobs:
run: |
source scripts/e2e-setup-ci.sh
echo "registry=http://localhost:4873
loglevel="silent"
loglevel="warn"
fetch-retries=10
fetch-retry-factor=2
fetch-retry-mintimeout=10000

16
.pnp.js generated
View file

@ -84,7 +84,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@verdaccio/local-storage", "npm:10.2.1"],
["@verdaccio/readme", "npm:10.3.4"],
["@verdaccio/streams", "npm:10.2.0"],
["@verdaccio/types", "npm:10.3.0"],
["@verdaccio/types", "npm:10.4.2"],
["@verdaccio/ui-theme", "npm:6.0.0-6-next.24"],
["JSONStream", "npm:1.3.5"],
["all-contributors-cli", "npm:6.20.0"],
@ -5865,10 +5865,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]
]],
["@verdaccio/types", [
["npm:10.3.0", {
"packageLocation": "./.yarn/cache/@verdaccio-types-npm-10.3.0-a7d2915e19-307c485a51.zip/node_modules/@verdaccio/types/",
["npm:10.4.2", {
"packageLocation": "./.yarn/cache/@verdaccio-types-npm-10.4.2-78b2370b99-f4f02e1496.zip/node_modules/@verdaccio/types/",
"packageDependencies": [
["@verdaccio/types", "npm:10.3.0"]
["@verdaccio/types", "npm:10.4.2"]
],
"linkType": "HARD",
}]
@ -8090,14 +8090,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
]],
["core-js", [
["npm:2.6.9", {
"packageLocation": "./.yarn/cache/core-js-npm-2.6.9-f821bf686c-00c30207eb.zip/node_modules/core-js/",
"packageLocation": "./.yarn/unplugged/core-js-npm-2.6.9-f821bf686c/node_modules/core-js/",
"packageDependencies": [
["core-js", "npm:2.6.9"]
],
"linkType": "HARD",
}],
["npm:3.22.4", {
"packageLocation": "./.yarn/cache/core-js-npm-3.22.4-4469b89edf-1305b2b9c1.zip/node_modules/core-js/",
"packageLocation": "./.yarn/unplugged/core-js-npm-3.22.4-4469b89edf/node_modules/core-js/",
"packageDependencies": [
["core-js", "npm:3.22.4"]
],
@ -15230,7 +15230,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
]],
["puppeteer", [
["npm:5.5.0", {
"packageLocation": "./.yarn/cache/puppeteer-npm-5.5.0-bba75ba998-08ba8a7da5.zip/node_modules/puppeteer/",
"packageLocation": "./.yarn/unplugged/puppeteer-npm-5.5.0-bba75ba998/node_modules/puppeteer/",
"packageDependencies": [
["puppeteer", "npm:5.5.0"],
["debug", "virtual:fada3bd8ad326a7c196d0c24aae1d5410b84126805d4b297cac3abf549e077c61a437968e49905247d38e2ca430b4cee29c78b779ec928550ea7a1cdf2adc3c1#npm:4.1.1"],
@ -18010,7 +18010,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@verdaccio/local-storage", "npm:10.2.1"],
["@verdaccio/readme", "npm:10.3.4"],
["@verdaccio/streams", "npm:10.2.0"],
["@verdaccio/types", "npm:10.3.0"],
["@verdaccio/types", "npm:10.4.2"],
["@verdaccio/ui-theme", "npm:6.0.0-6-next.24"],
["JSONStream", "npm:1.3.5"],
["all-contributors-cli", "npm:6.20.0"],

Binary file not shown.

6
debug/bootstrap-runserver.js vendored Normal file
View file

@ -0,0 +1,6 @@
// this file aims to help local debugging with hot transpilation
// it requires BABEL_ENV=registry set as env variable
require('@babel/register')({
extensions: ['.ts', '.js'],
});
require('./run-server');

9
debug/run-server.ts Normal file
View file

@ -0,0 +1,9 @@
/* eslint-disable no-console */
const { runServer } = require('../src');
(async () => {
const app = await runServer(); // default configuration
app.listen(4000, () => {
console.log('listening on port 4000');
});
})();

View file

@ -105,7 +105,7 @@
"@typescript-eslint/eslint-plugin": "4.33.0",
"@typescript-eslint/parser": "4.33.0",
"@verdaccio/eslint-config": "^8.5.0",
"@verdaccio/types": "10.3.0",
"@verdaccio/types": "10.4.2",
"all-contributors-cli": "6.20.0",
"babel-eslint": "10.1.0",
"babel-jest": "26.6.3",
@ -172,7 +172,9 @@
"lint:ts": "eslint \"**/*.{js,jsx,ts,tsx}\" -c ./eslintrc.js",
"lint:lockfile": "lockfile-lint --path yarn.lock --type yarn --validate-https --allowed-hosts verdaccio npm yarn",
"start": "yarn babel-node --extensions \".ts,.tsx\" src/lib/cli --inspect",
"start:brk": "yarn babel-node --extensions \".ts,.tsx\" src/lib/cli --inspect-brk",
"start:debug": "yarn node debug/bootstrap.js",
"start:run-server": "yarn node debug/bootstrap-runserver.js",
"code:build": "yarn babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps inline",
"code:docker-build": "yarn babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\"",
"docker": "docker build -t verdaccio/verdaccio:local . --no-cache",

View file

@ -7,31 +7,21 @@ auth:
htpasswd:
file: ./htpasswd
uplinks:
verdaccio:
url: https://registry.verdaccio.org/
max_fails: 30
fail_timeout: 10m
timeout: 60s
cache: false
maxage: 30m
agent_options:
keepAlive: true
maxSockets: 40
maxFreeSockets: 10
npmjs:
url: https://registry.npmjs.org/
packages:
'@*/*':
# scoped packages
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: verdaccio
proxy: npmjs
'**':
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: verdaccio
proxy: npmjs
server:
keepAliveTimeout: 60
@ -40,5 +30,4 @@ middlewares:
audit:
enabled: true
logs:
- { type: stdout, format: json, level: warn }
logs: { type: stdout, format: json, level: warn }

View file

@ -1,4 +1,5 @@
// @flow
import { startVerdaccio } from './lib/bootstrap';
export default startVerdaccio;
export { parseConfigFile } from './lib/utils';
export { startVerdaccio as default, startVerdaccio } from './lib/bootstrap';
// Similar structure as v6 but with different functions
// this is a bridge for easy migration to v6
export { runServer } from './lib/bootstrap.v2';

View file

@ -1,6 +1,6 @@
import constants from 'constants';
import express from 'express';
import { Application } from 'express';
import express from 'express';
import fs from 'fs';
import http from 'http';
import https from 'https';
@ -32,6 +32,7 @@ function displayExperimentsInfoBox(experiments) {
* @param {String} configPath
* @param {String} pkgVersion
* @param {String} pkgName
* @deprecated use runServer instead
*/
function startVerdaccio(config: any, cliListen: string, configPath: string, pkgVersion: string, pkgName: string, callback: Callback): void {
if (isObject(config) === false) {
@ -45,6 +46,10 @@ function startVerdaccio(config: any, cliListen: string, configPath: string, pkgV
endPointAPI(config).then((app): void => {
const addresses = getListListenAddresses(cliListen, config.listen);
if (addresses.length > 1) {
process.emitWarning('multiple listen addresses are deprecated, please use only one');
}
addresses.forEach(function (addr): void {
let webServer;
if (addr.proto === 'https') {
@ -135,7 +140,14 @@ function handleHTTPS(app: express.Application, configPath: string, config: Confi
process.exit(2);
}
}
/**
*
* @param webServer
* @param addr
* @param pkgName
* @param pkgVersion
* @deprecated use initServer instead
*/
function listenDefaultCallback(webServer: Application, addr: any, pkgName: string, pkgVersion: string): void {
const server = webServer
.listen(addr.port || addr.path, addr.host, (): void => {

147
src/lib/bootstrap.v2.ts Normal file
View file

@ -0,0 +1,147 @@
import constants from 'constants';
import buildDebug from 'debug';
import fs from 'fs';
import http from 'http';
import https from 'https';
import _, { assign } from 'lodash';
import path from 'path';
import { ConfigRuntime, HttpsConfKeyCert, HttpsConfPfx } from '@verdaccio/types';
import endPointAPI from '../api/index';
import { getListListenAddresses } from './cli/utils';
import findConfigFile from './config-path';
import { API_ERROR } from './constants';
import { parseConfigFile } from './utils';
const debug = buildDebug('verdaccio');
const logger = require('./logger');
export function displayExperimentsInfoBox(flags) {
if (!flags) {
return;
}
const experimentList = Object.keys(flags);
if (experimentList.length >= 1) {
logger.warn(
// eslint-disable-next-line max-len
`experiments are enabled, it is recommended do not use experiments in production comment out this section to disable it`
);
experimentList.forEach((experiment) => {
// eslint-disable-next-line max-len
logger.info(`support for experiment [${experiment}] ${flags[experiment] ? 'is enabled' : ' is disabled'}`);
});
}
}
/**
* Exposes a server factory to be instantiated programmatically.
*
const app = await runServer(); // default configuration
const app = await runServer('./config/config.yaml');
const app = await runServer({ configuration });
app.listen(4000, (event) => {
// do something
});
* @param config
*/
export async function runServer(config?: string): Promise<any> {
let configurationParsed: ConfigRuntime;
if (config === undefined || typeof config === 'string') {
const configPathLocation = findConfigFile(config);
configurationParsed = parseConfigFile(configPathLocation) as ConfigRuntime;
if (!configurationParsed.self_path) {
configurationParsed.self_path = path.resolve(configPathLocation);
}
} else if (_.isObject(config)) {
configurationParsed = config;
if (!configurationParsed.self_path) {
throw new Error('self_path is required, please provide a valid root path for storage');
}
} else {
throw new Error(API_ERROR.CONFIG_BAD_FORMAT);
}
const addresses = getListListenAddresses(undefined, configurationParsed.listen);
if (addresses.length > 1) {
process.emitWarning('You have specified multiple listen addresses, using this method only the first will be used');
}
const app = await endPointAPI(configurationParsed);
return createServerFactory(configurationParsed, addresses[0], app);
}
/**
* Return a native HTTP/HTTPS server instance
* @param config
* @param addr
* @param app
*/
export function createServerFactory(config: ConfigRuntime, addr, app) {
let serverFactory;
if (addr.proto === 'https') {
debug('https enabled');
try {
let httpsOptions = {
// disable insecure SSLv2 and SSLv3
secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3,
};
const keyCertConfig = config.https as HttpsConfKeyCert;
const pfxConfig = config.https as HttpsConfPfx;
// https must either have key and cert or a pfx and (optionally) a passphrase
if (!((keyCertConfig.key && keyCertConfig.cert) || pfxConfig.pfx)) {
throw Error('bad format https configuration');
}
if (pfxConfig.pfx) {
const { pfx, passphrase } = pfxConfig;
httpsOptions = assign(httpsOptions, {
pfx: fs.readFileSync(pfx),
passphrase: passphrase || '',
});
} else {
const { key, cert, ca } = keyCertConfig;
httpsOptions = assign(httpsOptions, {
key: fs.readFileSync(key),
cert: fs.readFileSync(cert),
...(ca && {
ca: fs.readFileSync(ca),
}),
});
}
// TODO: enable http2 as feature
// if (config.server.http2) <-- check if force http2
serverFactory = https.createServer(httpsOptions, app);
} catch (err) {
throw new Error(`cannot create https server: ${err.message}`);
}
} else {
// http
debug('http enabled');
serverFactory = http.createServer(app);
}
if (
config.server &&
typeof config.server.keepAliveTimeout !== 'undefined' &&
// @ts-ignore
config.server.keepAliveTimeout !== 'null'
) {
// library definition for node is not up to date (doesn't contain recent 8.0 changes)
serverFactory.keepAliveTimeout = config.server.keepAliveTimeout * 1000;
}
// FIXE: I could not find the reason of this code.
unlinkAddressPath(addr);
return serverFactory;
}
function unlinkAddressPath(addr) {
if (addr.path && fs.existsSync(addr.path)) {
fs.unlinkSync(addr.path);
}
}

View file

@ -27,7 +27,7 @@ export function isVersionValid(version) {
- localhost:5557
@return {Array}
*/
export function getListListenAddresses(argListen: string, configListen: any): any {
export function getListListenAddresses(argListen: string | void, configListen: any): any {
// command line || config file || default
let addresses;
if (argListen) {

View file

@ -1,12 +1,14 @@
import buildDebug from 'debug';
import fs from 'fs';
import _ from 'lodash';
import mkdirp from 'mkdirp';
import Path from 'path';
import { CHARACTER_ENCODING } from './constants';
import { logger } from './logger';
import { fileExists, folderExists } from './utils';
const debug = buildDebug('verdaccio:config');
const CONFIG_FILE = 'config.yaml';
const XDG = 'xdg';
const WIN = 'win';
@ -23,19 +25,20 @@ export type SetupDirectory = {
* Find and get the first config file that match.
* @return {String} the config file path
*/
function findConfigFile(configPath: string): string {
if (_.isNil(configPath) === false) {
function findConfigFile(configPath?: string): string {
if (typeof configPath !== 'undefined') {
return Path.resolve(configPath);
}
const configPaths: SetupDirectory[] = getConfigPaths();
debug('%o posible locations found', configPaths.length);
if (_.isEmpty(configPaths)) {
throw new Error('no configuration files can be processed');
}
const primaryConf: any = _.find(configPaths, (configLocation: any) => fileExists(configLocation.path));
if (_.isNil(primaryConf) === false) {
if (typeof primaryConf !== 'undefined') {
debug('previous location exist already %s', primaryConf?.path);
return primaryConf.path;
}

View file

@ -67,6 +67,7 @@ export function sendNotification(metadata: Package, notify: any, remoteUser: Rem
export function notify(metadata: Package, config: Config, remoteUser: RemoteUser, publishedPackage: string): Promise<any> | void {
if (config.notify) {
// @ts-ignore
if (config.notify.content) {
return sendNotification(metadata, config.notify as unknown as any, remoteUser, publishedPackage);
}

View file

@ -0,0 +1,29 @@
web:
enable: true
title: verdaccio-bootstrap
store:
memory:
limit: 10
auth:
auth-memory:
users:
test:
name: test
password: test
logs: { type: stdout, format: pretty, level: warn }
packages:
'@*/*':
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs
'**':
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs

View file

@ -0,0 +1,28 @@
import { join } from 'path';
import startVerdaccioDeault, { startVerdaccio } from '../../../../src';
import { parseConfigFile } from '../../../../src/lib/utils';
describe('bootstrap legacy', () => {
describe('startVerdaccio', () => {
test('run server should be able to listen', (done) => {
const p = join(__dirname, './config.yaml');
const cache = join(__dirname, 'cache');
const config = parseConfigFile(p);
startVerdaccio(config, '5000', cache, '1.0.0', 'verdaccio', (server, addr) => {
server.close();
done();
});
});
test('run server should be able to listen default method', (done) => {
const p = join(__dirname, './config.yaml');
const cache = join(__dirname, 'cache');
const config = parseConfigFile(p);
startVerdaccioDeault(config, '5000', cache, '1.0.0', 'verdaccio', (server, addr) => {
server.close();
done();
});
});
});
});

View file

@ -0,0 +1,48 @@
import { join } from 'path';
import { runServer } from '../../../../src';
import { parseConfigFile } from '../../../../src/lib/utils';
describe('bootstrap modern', () => {
describe('runServer', () => {
test('run server should be able to listen', (done) => {
const configPath = join(__dirname, './config.yaml');
runServer(configPath).then((app) => {
app.listen(4000, () => {
app.close();
done();
});
});
});
test('run server should be able to listen with object', (done) => {
const configPath = join(__dirname, './config.yaml');
const c = parseConfigFile(configPath);
// workaround
// on v5 the `self_path` still exists and will be removed in v6
c.self_path = 'foo';
runServer(c).then((app) => {
app.listen(4000, () => {
app.close();
done();
});
});
});
test('run server should be able to listen async', async () => {
const configPath = join(__dirname, './config.yaml');
const app = await runServer(configPath);
return new Promise((resolve) => {
app.listen(4000, () => {
app.close();
resolve(true);
});
});
});
test('run server should fails with wrong path', async () => {
const configPath = join(__dirname, './this_does_not_exist.yaml');
await expect(runServer(configPath)).rejects.toThrow(/Error: CONFIG: it does not look like a valid config file/);
});
});
});

BIN
yarn.lock

Binary file not shown.