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 path to deploy on the target server') .option('-d, --dist ', 'dist folder 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 dist=commander.dist||'dist' // 新系统将发布在这个目录里。建议和npm run build产生的目录一致,这样既可以远程自动部署,也可以直接登录服务器手动部署。 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 () => { await ssh.execCommand(`mv ${dist} ${dist}-backup-${new Date().toISOString()}`, { root }); // 把服务器上老系统改名成另一个目录 await ssh.execCommand(`mkdir ${dist}`, { root }); // 新建发布目录 const toCreate = necessaryPath('./dist'); // 读取本地项目的发布目录下的子目录,去服务器上建立。 for (const name of toCreate) { await ssh.execCommand(`mkdir ${dist}/${name.join('/')}`, { root }); } let err; await ssh.putDirectory('./dist', `${root}/${dist}`, { 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); });