diff --git a/deploy.js b/deploy.js new file mode 100644 index 0000000..624e62b --- /dev/null +++ b/deploy.js @@ -0,0 +1,82 @@ +const fs = require('fs'); +const path = require('path'); +const NodeSsh = require('node-ssh'); +const ssh = new NodeSsh(); +const commander = require('commander') + +commander +.version('1.0', '-v, --version') // 默认是 -V。如果要 -v,就要加 '-v --version' +.option('-H, --host ', 'domain name or ip address of the target server') +.option('-P, --port ', 'ssh port number of the target server') +.option('-r, --root ', 'root directory to deploy on the target server') +.option('-u, --user ', 'user id to login the target server') +.option('-k, --key ', 'user key file to login the target server') +.option('-p, --password ', 'user password to login the target server. You may have to enclose the password in ""') +.parse(process.argv) + +const root=commander.root||'/home/tic/tic.node/console' // 本地的项目目录。似乎该目录必须已经存在于服务器上 + +const privateKeyFile=commander.key||`${process.env.HOME}/.ssh/id_rsa` +const connection = { + host: commander.host, + port: commander.port||22, + username: commander.user||'tic', + privateKey: fs.existsSync(privateKeyFile)?privateKeyFile:undefined, + password: commander.password, +}; + +function subDirs(path) { + const dirs = [path]; + if (fs.statSync(path).isFile()) { + return dirs; + } + fs.readdirSync(path).forEach(item => { + const stat = fs.statSync(`${path}/${item}`); + if (stat.isDirectory()) { + dirs.push(...subDirs(`${path}/${item}`)) + } + }); + return dirs; +} + +const necessaryPath = (path) => { + return subDirs(path) + .map(it => it.replace(path, '')) + .filter(it => it) + .map(it => it.split('/').filter(it => it)); +}; + +ssh.connect(connection).then(async () => { + const proj = 'dist' // 新系统将发布在这个目录里。建议和npm run build产生的目录一致,这样既可以远程自动部署,也可以直接登录服务器手动部署。 + await ssh.execCommand(`mv ${proj} ${proj}-backup-${new Date().toISOString()}`, { root }); // 把服务器上老系统改名成另一个目录 + await ssh.execCommand(`mkdir ${proj}`, { root }); // 新建发布目录 + const toCreate = necessaryPath('./dist'); // 读取本地项目的发布目录下的子目录,去服务器上建立。 + for (const name of toCreate) { + await ssh.execCommand(`mkdir ${proj}/${name.join('/')}`, { root }); + } + + let err; + await ssh.putDirectory('./dist', `${root}/${proj}`, { + concurrency: 10, + recursive: true, + validate: itemPath => { + const baseName = path.basename(itemPath); + return !baseName.endsWith('.map'); + }, + tick: (localPath, remotePath, error) => { + console.log(`file ${localPath} to ${remotePath} ${error || 'uploaded'}`); + err = error; + }, + }); + ssh.dispose(); + if (err) { + console.log('uploaded with error!'); + process.exit(1); + } else { + console.log('build published!'); + } +}).catch(err => { + console.error(err); + ssh.dispose(); + process.exit(1); +}); diff --git a/package.json b/package.json index 1b8799f..3458acc 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "babel-jest": "^23.0.1", "babel-plugin-transform-imports": "^1.5.1", "node-sass": "^4.9.0", + "node-ssh": "^5.1.2", "sass-loader": "^7.0.1", "stylus": "^0.54.5", "stylus-loader": "^3.0.2", diff --git a/server.js b/server.js index 2201a0a..8025a6e 100644 --- a/server.js +++ b/server.js @@ -28,7 +28,7 @@ try { // 载入命令行参数 commander .version(Config.VERSION, '-v, --version') // 默认是 -V。如果要 -v,就要加 '-v --version' - .option('-H, --host ', 'host ip or domain name') + .option('-h, --host ', 'host ip or domain name') .option('-P, --protocol ', 'Web server protocol http|https|httpall, default to httpall') .option('-p, --port ', 'Server port, default to 80|443') .option('--sslCert ', 'SSL cert file') @@ -37,8 +37,8 @@ try { .parse(process.argv) // 把命令行参数 合并入配置。 - Config.host=commander.host || Config.host - Config.protocol=commander.protocol || Config.protocol // 默认同时启用 http 和 https + Config.host=commander.host || Config.host || 'localhost' + Config.protocol=commander.protocol || Config.protocol || 'httpall' // 默认同时启用 http 和 https Config.port=parseInt(commander.port) || parseInt(Config.port) || (Config.protocol==='http'?80:Config.protocol==='https'?443:undefined) // 端口默认为 http:80, https:443, httpall: 80|443 Config.sslCert=commander.sslCert || Config.sslCert Config.sslKey=commander.sslKey || Config.sslKey @@ -76,12 +76,16 @@ async function init(){ /*** 设置全局对象 ***/ server.use(compression()) /*** 路由 ***/ - server.use(express.static(path.join(__dirname,'dist'), {index:'index.html'})) + //server.use(express.static(path.join(__dirname,'dist'), {index:'index.html'})) -// var vhost = require('vhost') -// var webroot=express.static(path.join(__dirname,'dist'), {index:'index.html'}) + var webroot=express.static(path.join(__dirname,'dist'), {index:'index.html'}) + server.use(webroot) + + // var vhost = require('vhost') // server.use(vhost('bittic.org', webroot)) // server.use(vhost('www.bittic.org', webroot)) + // server.use(vhost('box.bittic.org', express.static(path.join(__dirname,'../ticbox/dist'), {index:'index.html'}))) + // server.use(vhost('wallet.bittic.org', express.static(path.join(__dirname,'../ticwalletx/dist'), {index:'index.html'}))) // server.use(vhost('*', webroot)) // server.use(vhost('*.*', webroot)) // server.use(vhost('*.*.*', webroot)) @@ -111,3 +115,5 @@ async function init(){ /*** 设置全局对象 ***/ console.log('Server listening on %s://%s:%d for %s environment', wo.Config.protocol, wo.Config.host, portHttps, server.settings.env) }) } + +})() \ No newline at end of file