From 7e810dba4833289f11d531fb94907b2d9f11125d Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Tue, 3 Feb 2015 23:05:15 +0800 Subject: [PATCH] docs: Deploy a private npm registry in 5 minutes --- .npmignore | 1 + README.md | 3 +- bin/cli.js | 131 ++++++++++++++++++++++++++++++ config/index.js | 24 ++++-- controllers/sync_module_worker.js | 8 +- dispatch.js | 3 + package.json | 8 +- 7 files changed, 162 insertions(+), 16 deletions(-) create mode 100755 bin/cli.js diff --git a/.npmignore b/.npmignore index da60d58..b7f8d72 100644 --- a/.npmignore +++ b/.npmignore @@ -22,3 +22,4 @@ config/web_readme.md config/config.js *.sqlite .node-dev.json +nohup.out diff --git a/README.md b/README.md index b966aeb..5053b1f 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Our goal is to provide a low cost maintenance and easy to use solution for priva * Build a mirror NPM. (we use it to build a mirror in China: [cnpmjs.org](http://cnpmjs.org/)) * Build a completely independent NPM registry to store whatever you like. -### Features +## Features * **Support "scoped" packages**: [npm/npm#5239](https://github.com/npm/npm/issues/5239) * **Support [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing)** @@ -66,6 +66,7 @@ as well as [New features in 2.x](https://github.com/cnpm/cnpmjs.org/wiki/New-fea ## Getting Start +* [Deploy a private npm registry in 5 minutes](https://github.com/cnpm/cnpmjs.org/wiki/Deploy-a-private-npm-registry-in-5-minutes) * @[dead-horse](https://github.com/dead-horse): [What is cnpm?](http://deadhorse.me/slides/cnpmjs.html) * install and deploy cnpmjs.org through npm: [examples](https://github.com/cnpm/custom-cnpm-example) * Mirror NPM in China: [cnpmjs.org](http://cnpmjs.org) diff --git a/bin/cli.js b/bin/cli.js new file mode 100755 index 0000000..3edd339 --- /dev/null +++ b/bin/cli.js @@ -0,0 +1,131 @@ +#!/usr/bin/env node --harmony + +/**! + * cnpmjs.org - bin/cli.js + * + * Copyright(c) fengmk2 and other contributors. + * MIT Licensed + * + * Authors: + * fengmk2 (http://fengmk2.com) + */ + +'use strict'; + +/** + * Module dependencies. + */ + +var debug = require('debug')('cnpmjs.org:cli'); +var program = require('commander'); +var path = require('path'); +var fs = require('fs'); +var mkdirp = require('mkdirp'); +var treekill = require('treekill'); +var version = require('../package.json').version; + +function list(val) { + return val.split(','); +} + +program + .version(version); + +program + .command('start') + .description('start cnpmjs.org server') + .option('--admins ', 'set admins', list) + .option('--scopes ', 'set scopes', list) + // .option('--cluster', 'enable cluster mode') + .option('--dataDir ', 'cnpmjs.org data dir, default is `$HOME/.cnpmjs.org`') + .action(start); + +program + .command('stop') + .description('stop cnpmjs.org server') + .option('--dataDir ', 'cnpmjs.org data dir, default is `$HOME/.cnpmjs.org`') + .action(stop); + +program.parse(process.argv); + + +function start(options) { + var dataDir = options.dataDir || path.join(process.env.HOME, '.cnpmjs.org'); + mkdirp.sync(dataDir); + + var configfile = path.join(dataDir, 'config.json'); + var config = {}; + if (fs.existsSync(configfile)) { + try { + config = require(configfile); + } catch (err) { + console.warn('load old %s error: %s', configfile, err); + } + } + // config.enableCluster = !!options.cluster; + if (options.admins) { + config.admins = {}; + for (var i = 0; i < options.admins.length; i++) { + config.admins[options.admins[i]] = options.admins[i] + '@localhost.com'; + } + } + if (options.scopes) { + config.scopes = options.scopes.map(function (name) { + if (name[0] !== '@') { + name = '@' + name; + } + return name; + }); + } + + var configJSON = JSON.stringify(config, null, 2); + fs.writeFileSync(configfile, configJSON); + + debug('save config %s to %s', configJSON, configfile); + + // if sqlite db file not exists, init first + initDatabase(); + + require('../dispatch'); + fs.writeFileSync(path.join(dataDir, 'pid'), process.pid + ''); +} + +function stop(options) { + var dataDir = options.dataDir || path.join(process.env.HOME, '.cnpmjs.org'); + var pidfile = path.join(dataDir, 'pid'); + if (fs.existsSync(pidfile)) { + var pid = Number(fs.readFileSync(pidfile, 'utf8')); + treekill(pid, function (err) { + if (err) { + console.log(err); + throw err; + } + console.log('cnpmjs.org server:%d stop', pid); + fs.unlinkSync(pidfile); + }); + } else { + console.log('cnpmjs.org server not start'); + } +} + +function initDatabase() { + var models = require('../models'); + + models.sequelize.sync({ force: false }) + .then(function () { + models.Total.init(function (err) { + if (err) { + console.error('[models/init_script.js] sequelize init fail'); + console.error(err); + throw err; + } else { + console.log('[models/init_script.js] `sqlite` sequelize sync and init success'); + } + }); + }) + .catch(function (err) { + console.error('[models/init_script.js] sequelize sync fail'); + console.error(err); + throw err; + }); +} diff --git a/config/index.js b/config/index.js index b8bbad8..71119fe 100644 --- a/config/index.js +++ b/config/index.js @@ -18,7 +18,6 @@ var mkdirp = require('mkdirp'); var copy = require('copy-to'); var path = require('path'); -var fs = require('fs'); var os = require('os'); var version = require('../package.json').version; @@ -159,10 +158,7 @@ var config = { enablePrivate: false, // registry scopes, if don't set, means do not support scopes - scopes: [ - '@cnpm', - '@cnpmtest' - ], + scopes: [ '@cnpm', '@cnpmtest' ], // some registry already have some private packages in global scope // but we want to treat them as scoped private packages, @@ -219,10 +215,20 @@ var config = { userService: null, }; -// load config/config.js, everything in config.js will cover the same key in index.js -var customConfig = path.join(root, 'config/config.js'); -if (fs.existsSync(customConfig)) { - copy(require(customConfig)).override(config); +if (process.env.NODE_ENV !== 'test') { + // 1. try to load `$dataDir/config.js` first, not exists then goto 2. + // 2. load config/config.js, everything in config.js will cover the same key in index.js + var customConfig = path.join(dataDir, 'config'); + try { + copy(require(customConfig)).override(config); + } catch (err) { + customConfig = path.join(root, 'config', 'config'); + try { + copy(require(customConfig)).override(config); + } catch (err) { + // ignore + } + } } mkdirp.sync(config.logdir); diff --git a/controllers/sync_module_worker.js b/controllers/sync_module_worker.js index 49687d7..8a2d7d5 100644 --- a/controllers/sync_module_worker.js +++ b/controllers/sync_module_worker.js @@ -146,10 +146,6 @@ SyncModuleWorker.prototype.start = function () { return; } - if (config.syncModel === 'none') { - return; - } - var arr = []; for (var i = 0; i < that.concurrency; i++) { arr.push(that.next(i)); @@ -283,6 +279,10 @@ SyncModuleWorker.prototype.syncUser = function* () { }; SyncModuleWorker.prototype.next = function* (concurrencyId) { + if (config.syncModel === 'none') { + return this.finish(); + } + var name = this.names.shift(); if (!name) { return setImmediate(this.finish.bind(this)); diff --git a/dispatch.js b/dispatch.js index 7ebcfac..aff1146 100644 --- a/dispatch.js +++ b/dispatch.js @@ -23,6 +23,9 @@ var config = require('./config'); var workerPath = path.join(__dirname, 'worker.js'); var syncPath = path.join(__dirname, 'sync'); +console.log('Starting cnpmjs.org ...\ncluster: %s\nadmins: %j\nscopes: %j\nsourceNpmRegistry: %s', + config.enableCluster, config.admins, config.scopes, config.sourceNpmRegistry); + if (config.enableCluster) { forkWorker(); if (config.syncModel !== 'none') { diff --git a/package.json b/package.json index 4109cab..39f8fb7 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,9 @@ "status": "./bin/nodejsctl status", "stop": "./bin/nodejsctl stop" }, + "bin": { + "cnpmjs.org": "bin/cli.js" + }, "dependencies": { "agentkeepalive": "~1.2.0", "bluebird": "~2.9.6", @@ -18,10 +21,11 @@ "co-defer": "~0.1.2", "co-gather": "~0.0.1", "co-sleep": "~0.0.1", + "commander": "~2.6.0", "copy-to": "~2.0.1", "debug": "~2.1.1", "error-formater": "~1.0.3", - "fs-cnpm": "~1.1.0", + "fs-cnpm": "~1.2.0", "giturl": "~0.0.3", "graceful": "~1.0.0", "gravatar": "~1.1.0", @@ -42,6 +46,7 @@ "semver": "~4.2.0", "sequelize": "~2.0.0-rc8", "thunkify-wrap": "~1.0.4", + "treekill": "~1.0.0", "urllib": "~2.2.2", "utility": "~1.3.0", "xss": "~0.1.20" @@ -53,7 +58,6 @@ "contributors": "*", "istanbul-harmony": "*", "jshint": "*", - "koa-mock": "~1.1.4", "mm": "*", "mocha": "*", "node-dev": "*",