Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Init #1

Merged
merged 13 commits into from
Oct 14, 2016
Merged
25 changes: 25 additions & 0 deletions .autod.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';

module.exports = {
write: true,
prefix: '^',
test: [
'test',
'benchmark',
],
devdep: [
'egg-ci',
'egg-bin',
'autod',
'eslint',
'eslint-config-egg',
'supertest',
'power-assert',
'intelli-espower-loader',
'egg-view-nunjucks',
],
exclude: [
'./test/fixtures',
],
};

2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test/fixtures
coverage
3 changes: 3 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "eslint-config-egg"
}
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
logs/
npm-debug.log
node_modules/
coverage/
.idea/
run/
.DS_Store
*.swp

11 changes: 11 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
sudo: false
language: node_js
node_js:
- '4'
- '6'
install:
- npm i npminstall && npminstall
script:
- npm run ci
after_script:
- npminstall codecov && codecov
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
# Nodeinstall



## QuickStart

```shell
$ npm install
$ npm run dev
$ open http://localhost:7001/news
```

## Questions & Suggestions

Please open an issue [here](https://github.com/eggjs/egg/issues).
15 changes: 15 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
environment:
matrix:
- nodejs_version: '4'
- nodejs_version: '6'

install:
- ps: Install-Product node $env:nodejs_version
- npm i npminstall && node_modules\.bin\npminstall

test_script:
- node --version
- npm --version
- npm run ci

build: off
7 changes: 7 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

const co = require('co');
exports.install = co(require('./lib/install'));
exports.installNode = co(require('./lib/install_node'));
exports.installAlinode = co(require('./lib/install_alinode'));
exports.installNsolid = co(require('./lib/install_nsolid'));
48 changes: 48 additions & 0 deletions lib/cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';

const os = require('os');
const path = require('path');

let homedir = process.env.HOME;
// for Windows HOME
if (!homedir) {
homedir = process.env.HOMEDRIVE + process.env.HOMEPATH;
}
if (!homedir) {
homedir = os.tmpdir();
}

const tmpdir = path.join(homedir, '.tmp');
let cachedir = process.env.NODEINSTALL_CACHE;
if (!cachedir) {
if (process.platform === 'win32') {
cachedir = path.join(process.env.APPDATA || tmpdir, '.nodeinstall');
} else {
cachedir = path.join(process.env.HOME || tmpdir, '.nodeinstall');
}
}

// 获取缓存策略
exports.getStrategy = function() {
// http://gitlab.alibaba-inc.com/node/tnpm/issues/24
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

�内网地址删除了吧

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

内网地址删除了。。

// 避免网络问题导致 tnpm install 变慢,默认在本地开发环境开启 cache 功能。
let disable = false;
if (process.argv.indexOf('--production') >= 0) {
disable = true;
}
if (process.env.NODE_ENV) {
// NODE_ENV 这个环境变量存在,代表当前运行环境是服务器,关闭 cache
disable = true;
}

let cache = cachedir;
if (disable) {
// 一次性缓存目录
// http://gitlab.alibaba-inc.com/node/tnpm/issues/44
// tnpm.$yyyy-MM-dd.$pid.$timestamp
const now = new Date();
const yyyyMMdd = now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate();
cache = path.join(tmpdir, 'nodeinstall.' + yyyyMMdd + '.' + process.pid + '.' + Date.now());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

install-node 关闭缓存不需要这种一次性缓存目录的

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

那应该如何处理呢,直接 pipe 到 extracter?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

放到真正的临时目录?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

恩,那也要建个一次性缓存目录

}
return { disable, cache };
};
14 changes: 14 additions & 0 deletions lib/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict';


module.exports = {
nodeDistUrl: 'https://nodejs.org/dist',
nodeDistUrlMirror: 'https://npm.taobao.org/mirrors/node',
nodeRcDistUrl: 'https://nodejs.org/download/rc',
nodeRcDistUrlMirror: 'https://npm.taobao.org/mirrors/node-rc',
nodeNightlyDistUrl: 'https://nodejs.org/download/nightly',
nodeNightlyDistUrlMirror: 'https://npm.taobao.org/mirrors/node-nightly',
alinodeDistUrl: 'http://alinode.aliyun.com/dist/new-alinode',
alinodeDistUrlMirror: 'https://npm.taobao.org/mirrors/alinode',
nsolidDistUrl: 'https://nsolid-download.nodesource.com/download/nsolid-node/release',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good

};
186 changes: 186 additions & 0 deletions lib/install.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
'use strict';

const os = require('os');
const path = require('path');
const fs = require('fs');
const bytes = require('bytes');
const urlparse = require('url').parse;
const ProgressBar = require('progress');
const crypto = require('crypto');
const zlib = require('zlib');
const tar = require('tar');
const mkdirp = require('mkdirp');
const debug = require('debug')('nodeinstall');
const request = require('./request');
const cache = require('./cache');
const getLocalNodeVersion = require('./version').getLocalNodeVersion;


module.exports = function* install(options) {
const distUrl = options.distUrl;
const version = options.version;
const name = options.name;
const cwd = options.cwd;

debug('Start install %s@%s, options: %j', name, version, options);

const nodeModulesDir = path.join(cwd, 'node_modules');
const nodeDir = path.join(nodeModulesDir, 'node');
const nodeLinkDir = path.join(nodeModulesDir, '.bin');
const nodeLink = path.join(nodeLinkDir, 'node');
const npmLink = path.join(nodeLinkDir, 'npm');

const platform = process.env.MOCK_OS_PLATFORM || os.platform();
const arch = os.arch() === 'x64' ? 'x64' : 'x86';
const shaUrl = `${distUrl}/v${version}/SHASUMS256.txt`;
const tgzUrl = `${distUrl}/v${version}/${name}-v${version}-${platform}-${arch}.tar.gz`;

try {
// process.versions from installed node
const versions = getLocalNodeVersion(cwd);
if (version === versions[name]) {
console.info('%s has been installed, version %s', name, version);
return;
}
} catch (e) {
if (e.name !== 'NodeNotInstalledError') {
throw e;
}
}

// use cache or not
const cacheStrategy = cache.getStrategy();
debug('cacheStrategy, %j', cacheStrategy);
const targetName = path.basename(urlparse(tgzUrl).pathname);
const targetFile = path.join(cacheStrategy.cache, targetName);

try {
// download tarball if cache is disabled or cache file is not exist,
// otherwise use cache
if (cacheStrategy.disable || fs.existsSync(targetFile)) {
yield downloadTgz(tgzUrl, shaUrl, targetFile);
}

// extract tarball to $cwd/node_modules
yield extract(targetFile, nodeDir);
} catch (err) {
if (fs.existsSync(targetFile)) {
fs.unlinkSync(targetFile);
}
throw err;
}

mkdirp.sync(nodeLinkDir);
fs.symlinkSync(path.normalize('../node/bin/node'), nodeLink);
if (!fs.existsSync(npmLink)) {
fs.symlinkSync(path.normalize('../node/bin/npm'), npmLink);
}
const packageJsonFile = path.join(nodeDir, 'package.json');
fs.writeFileSync(packageJsonFile, JSON.stringify({ name, version }, null, 2));
};

function* downloadTgz(tgzUrl, shaUrl, targetFile) {
debug('download %s to %s', tgzUrl, targetFile);
const ret = yield request(tgzUrl, {
timeout: 20000,
streaming: true,
followRedirect: true,
retry: 3,
});
const res = ret.res;

if (res.statusCode !== 200) {
const err = new Error('GET ' + tgzUrl + ' got ' + res.statusCode);
err.statusCode = res.statusCode;
err.headers = res.headers;
throw err;
}

yield writeFile(res, targetFile);
yield verifyChecksum(shaUrl, targetFile);
}

function* verifyChecksum(shaUrl, targetFile) {
const ret = yield request(shaUrl);
const res = ret.res;

if (res.statusCode !== 200) {
const err = new Error('GET ' + shaUrl + ' got ' + res.statusCode);
err.statusCode = res.statusCode;
throw err;
}

const tgzName = path.basename(targetFile);
const shas = ret.data.toString();
const refSha = getShaByTgzName(shas, tgzName);
const actualSha = calcSha(fs.readFileSync(targetFile));
if (refSha !== actualSha) {
const err = new Error(`Checksum verification failed. Reference checksum is ${refSha.slice(0, 7)}, but the actual checksum is ${actualSha.slice(0, 7)}`);
err.name = 'ChecksumError';
throw err;
}
debug('checksum success %s', refSha);
}

function extract(targetFile, nodeDir) {
return new Promise((resolve, reject) => {
const gunzip = zlib.createGunzip();
gunzip.on('error', reject);
const extracter = tar.Extract({ path: nodeDir, strip: 1 });
extracter.on('error', reject);
extracter.on('end', resolve);
fs.createReadStream(targetFile).pipe(gunzip).pipe(extracter);
});
}

function writeFile(res, targetFile) {
mkdirp.sync(path.dirname(targetFile));
return new Promise(function(resolve, reject) {
const ws = fs.createWriteStream(targetFile)
.on('error', reject)
.on('finish', resolve);
showProgress(res);
res.pipe(ws);
});
}

function showProgress(res) {
const bar = new ProgressBar('[nodeinstall] downloading [:bar] :percent :etas/:elapseds :current/:total :speed/s', {
complete: '=',
incomplete: ' ',
width: 20,
total: parseInt(res.headers['content-length'] || 1024 * 1024 * 30),
});

let start = Date.now();
let size = 0;
let speed = 0;
res.on('data', chunk => {
size += chunk.length;
const use = Date.now() - start;
// 每秒钟计算一次
if (use >= 1000) {
speed = size / use * 1000;
start = Date.now();
size = 0;
}
bar.tick(chunk.length, {
speed: bytes(speed || size),
});
});
}

function getShaByTgzName(content, tarName) {
let sha = '';
for (const line of content.split('\n')) {
if (line.indexOf(tarName) > -1) {
sha = line.split(/\s/)[0];
break;
}
}
return sha;
}

function calcSha(file) {
return crypto.createHash('sha256').update(file).digest('hex');
}
17 changes: 17 additions & 0 deletions lib/install_alinode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';

const getAlinodeVersion = require('./version').getAlinodeVersion;
const install = require('./install');
const config = require('./config');

const DEFAULT_OPTIONS = {
cwd: process.cwd(),
version: '',
distUrl: config.alinodeDistUrl,
};

module.exports = function* installAlinode(options) {
options = Object.assign({}, DEFAULT_OPTIONS, options, { name: 'alinode' });
options.version = yield getAlinodeVersion(options);
yield install(options);
};
17 changes: 17 additions & 0 deletions lib/install_node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';

const getNodeVersion = require('./version').getNodeVersion;
const install = require('./install');
const config = require('./config');

const DEFAULT_OPTIONS = {
cwd: process.cwd(),
version: '',
distUrl: config.nodeDistUrl,
};

module.exports = function* installNode(options) {
options = Object.assign({}, DEFAULT_OPTIONS, options, { name: 'node' });
options.version = yield getNodeVersion(options);
yield install(options);
};
Loading