wo-base-deployer/deploy.js

125 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require('fs')
const path = require('path')
const ssh = new (require('node-ssh'))()
/** ******************* 读取命令行以及配置文件里的参数 ******************** **/
const commander = require('commander')
const deepmerge = require('deepmerge')
var Config = {}
// 读取配置文件
try {
let configFile
if (fs.existsSync(configFile=path.join(process.cwd(), 'ConfigBasic.js'))) {
Config = require(configFile)
console.info(`${configFile} loaded`)
}
if (fs.existsSync(configFile=path.join(process.cwd(), 'ConfigCustom.js'))) { // 如果存在,覆盖掉 ConfigBasic 里的默认参数
Config = deepmerge(Config, require(configFile)) // 注意objectMerge后产生了一个新的对象而不是在原来的Config里添加
console.info(`${configFile} loaded`)
}
if (fs.existsSync(configFile=path.join(process.cwd(), 'ConfigSecret.js'))) { // 如果存在,覆盖掉 ConfigBasic 和 ConfigCustom 里的参数
Config = deepmerge(Config, require(pconfigFile))
console.info(`${configFile} loaded`)
}
} catch (err) {
console.error('Loading config files failed: ' + err.message)
}
commander
.version('1.0', '-v, --version') // 默认是 -V。如果要 -v就要加 '-v --version'
.option('-H, --host <host>', `Host IP or domain name of the target server. Default to ${Config.deploy.host}`)
.option('-P, --port <port>', `Ssh port number of the target server. Default to ${Config.deploy.port}`)
.option('-u, --user <user>', `User id to login the target server. Default to ${Config.deploy.user}`)
.option('-k, --key <key>', `User private key file to login the target server. Default to ${Config.deploy.key}`)
.option('-p, --password <password>', `User password to login the target server. You may have to enclose it in "". Default to "${Config.deploy.password}"`)
.option('-l, --local <folder>', `Local folder to copy from. Default to ${Config.deploy.local}`)
.option('-D, --dir <root>', `Directory to deploy on the target server. Default to ${Config.deploy.dir}`)
.option('-d, --dist <dist>', `Folder to deploy on the target server. Default to ${Config.deploy.dist}`)
.parse(process.argv)
const root = commander.root || Config.deploy.root // 本地的项目目录。似乎该目录必须已经存在于服务器上
console.log(` root = ${root} `)
const dist = commander.dist || Config.deploy.dist || 'dist' // 新系统将发布在这个目录里。建议为dist和npm run build产生的目录一致这样既可以远程自动部署也可以直接登录服务器手动部署。
console.log(` dist = ${dist} `)
const privateKeyFile = commander.key || Config.deploy.key || `${process.env.HOME}/.ssh/id_rsa`
console.log(` privateKeyFile = ${privateKeyFile} `)
const local = commander.local || Config.deploy.local || 'dist'
console.log(` local = ${local} `)
const connection = {
host: commander.host || Config.deploy.host,
port: commander.port || Config.deploy.port || 22,
username: commander.user || Config.deploy.user,
privateKey: fs.existsSync(privateKeyFile) ? privateKeyFile : undefined,
password: commander.password || Config.deploy.password,
tryKeyboard: true,
onKeyboardInteractive: (name, instructions, instructionsLang, prompts, finish) => { // 不起作用
if (prompts.length > 0 && prompts[0].prompt.toLowerCase().includes('password')) {
finish([password])
}
},
}
console.log(` connection = ${JSON.stringify(connection)}`)
/** ********************** 连接到待部署的主机,拷贝文件到指定路径 ************* **/
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 () => {
console.log(`[ mv ${dist} ${dist}-backup-${new Date().toISOString()} ... ]`)
await ssh.execCommand(`mv ${dist} ${dist}-backup-${new Date().toISOString()}`, { cwd: root })
console.log(`[ mkdir ${dist} ... ]`)
await ssh.execCommand(`mkdir ${dist}`, { cwd: root })
const toCreate = necessaryPath('./'+local)
for (const name of toCreate) {
console.log(`[ mkdir ${dist}/${name.join('/')} ... ]`)
await ssh.execCommand(`mkdir ${dist}/${name.join('/')}`, { cwd: root })
}
let err
console.log(`[ Upload to ${root}/${dist} ... ]`)
await ssh.putDirectory('./'+local, `${root}/${dist}`, {
concurrency: 10,
recursive: true,
validate: itemPath => {
const baseName = path.basename(itemPath)
return !baseName.endsWith('.map')
},
tick: (localPath, remotePath, error) => {
console.log(`Uploading "${localPath}" ===> "${remotePath}" ${error || 'succeeded!'}`)
err = error
},
})
ssh.dispose()
if (err) {
console.log('[ Uploaded with error! ]')
process.exit(1)
} else {
console.log('[ Uploaded successfully! ]')
}
}).catch(err => {
console.error(err)
ssh.dispose()
process.exit(1)
})