格式化

This commit is contained in:
陆柯 2022-06-05 13:30:21 +08:00
parent adb08df677
commit 816f2e3e82

405
deploy.js
View File

@ -20,7 +20,7 @@ const wo = (global.wo = {
targetDir: 'webroot', targetDir: 'webroot',
user: undefined, user: undefined,
password: undefined, password: undefined,
key: `${process.env.HOME}/.ssh/id_rsa`, key: `${process.env.HOME}/.ssh/id_rsa`
}, },
github: { github: {
targetType: 'git', targetType: 'git',
@ -30,7 +30,7 @@ const wo = (global.wo = {
gitemail: undefined, gitemail: undefined,
user: undefined, user: undefined,
password: undefined, password: undefined,
key: `${process.env.HOME}/.ssh/id_rsa`, key: `${process.env.HOME}/.ssh/id_rsa`
} }
} }
} }
@ -39,16 +39,24 @@ const wo = (global.wo = {
// 读取配置文件 // 读取配置文件
try { try {
let configFile let configFile
if (fs.existsSync(configFile=path.join(process.cwd(), 'envar-base-deploy.js'))) { if (
fs.existsSync(
(configFile = path.join(process.cwd(), 'envar-base-deploy.js'))
)
) {
wo.envar = deepmerge(wo.envar, require(configFile)) wo.envar = deepmerge(wo.envar, require(configFile))
console.info(`${configFile} loaded`) console.info(`- ${configFile} loaded`)
} }
if (fs.existsSync(configFile=path.join(process.cwd(), 'envar-base-secret.js'))) { if (
fs.existsSync(
(configFile = path.join(process.cwd(), 'envar-base-secret.js'))
)
) {
wo.envar = deepmerge(wo.envar, require(configFile)) wo.envar = deepmerge(wo.envar, require(configFile))
console.info(`${configFile} loaded`) console.info(`- ${configFile} loaded`)
} }
} catch (err) { } catch (err) {
console.error('Loading config files failed: ' + err.message) console.error('- Loading config files failed: ' + err.message)
} }
// 读取命令行参数 // 读取命令行参数
@ -61,8 +69,14 @@ commander
.option('-H, --host <host>', `Host IP or domain name of the target server.`) .option('-H, --host <host>', `Host IP or domain name of the target server.`)
.option('-P, --port <port>', `Ssh port number of the target server.`) .option('-P, --port <port>', `Ssh port number of the target server.`)
.option('-b, --targetPath <targetPath>', `Destination path to deploy on the target.`) .option(
.option('-d, --targetDir <targetDir>', `Destination folder to deploy on the target.`) '-d, --targetPath <targetPath>',
`Destination path to deploy on the target.`
)
.option(
'-D, --targetDir <targetDir>',
`Destination folder to deploy on the target.`
)
.option('-r, --repo <repo>', `git repo address.`) .option('-r, --repo <repo>', `git repo address.`)
.option('-b, --branch <branch>', `git repo branch.`) .option('-b, --branch <branch>', `git repo branch.`)
@ -70,12 +84,19 @@ commander
.option('-m, --gitemail <gitemail>', `git user email.`) .option('-m, --gitemail <gitemail>', `git user email.`)
.option('-u, --user <user>', `User id to login the target server.`) .option('-u, --user <user>', `User id to login the target server.`)
.option('-k, --key <key>', `User private key file to login the target server.`) .option(
.option('-p, --password <password>', `User password to login the target server. You may have to enclose it in "".`) '-k, --key <key>',
`User private key file to login the target server.`
)
.option(
'-p, --password <password>',
`User password to login the target server. You may have to enclose it in "".`
)
.parse(process.argv) .parse(process.argv)
wo.envar.deploy.fromPath = commander.fromPath || wo.envar.deploy.fromPath wo.envar.deploy.fromPath = commander.fromPath || wo.envar.deploy.fromPath
wo.envar.deploy.connection = wo.envar.deploy[commander.gotoTarget || wo.envar.deploy.gotoTarget] // 使用用户指定的连接 wo.envar.deploy.connection =
wo.envar.deploy[commander.gotoTarget || wo.envar.deploy.gotoTarget] // 使用用户指定的连接
// 可以用命令行参数覆盖掉配置文件 // 可以用命令行参数覆盖掉配置文件
const connection = { const connection = {
@ -93,27 +114,35 @@ const connection = {
gitemail: commander.gitemail || wo.envar.deploy.connection.gitemail, gitemail: commander.gitemail || wo.envar.deploy.connection.gitemail,
// common // common
username: commander.user || wo.envar.deploy.connection.user, username: commander.user || wo.envar.deploy.connection.user,
privateKey: fs.existsSync(commander.key || wo.envar.deploy.connection.key) ? (commander.key || wo.envar.deploy.connection.key) : undefined, privateKey: fs.existsSync(commander.key || wo.envar.deploy.connection.key)
? commander.key || wo.envar.deploy.connection.key
: undefined,
password: commander.password || wo.envar.deploy.connection.password, password: commander.password || wo.envar.deploy.connection.password,
tryKeyboard: true, tryKeyboard: true,
onKeyboardInteractive: (name, instructions, lang, prompts, finish) => { // 不起作用 onKeyboardInteractive: (name, instructions, lang, prompts, finish) => {
if (prompts.length > 0 && prompts[0].prompt.toLowerCase().includes('password')) { // 不起作用
if (
prompts.length > 0 &&
prompts[0].prompt.toLowerCase().includes('password')
) {
finish([password]) finish([password])
} }
}, },
url: wo.envar.deploy.connection.url url: wo.envar.deploy.connection.url
} }
console.log(` deploy from ${wo.envar.deploy.fromPath} to ${JSON.stringify(connection)}`) console.log(
` deploy from ${wo.envar.deploy.fromPath} to ${JSON.stringify(connection)}`
)
if (connection.targetType==='ssh') { if (connection.targetType === 'ssh') {
deployToSsh(connection) deployToSsh(connection)
}else if (connection.targetType==='git'){ } else if (connection.targetType === 'git') {
deployToGit(connection) deployToGit(connection)
} }
/** ********************** 连接到 Ssh主机拷贝文件到指定路径 ************* **/ /** ********************** 连接到 Ssh主机拷贝文件到指定路径 ************* **/
function deployToSsh(connection){ function deployToSsh (connection) {
const ssh = new (require('node-ssh'))() const ssh = new (require('node-ssh'))()
function subDirs (path) { function subDirs (path) {
@ -130,59 +159,91 @@ function deployToSsh(connection){
return dirs return dirs
} }
const necessaryPath = (path) => { const necessaryPath = path => {
return subDirs(path) return subDirs(path)
.map(it => it.replace(path, '')) .map(it => it.replace(path, ''))
.filter(it => it) .filter(it => it)
.map(it => it.split('/').filter(it => it)) .map(it => it.split('/').filter(it => it))
} }
ssh.connect(connection).then(async () => { ssh
console.log(`[ mv ${connection.targetDir} ${connection.targetDir}-backup-${new Date().toISOString()} ... ]`) .connect(connection)
await ssh.execCommand(`mv ${connection.targetDir} ${connection.targetDir}-backup-${new Date().toISOString()}`, { cwd: connection.targetPath }) .then(async () => {
console.log(`[ mkdir ${connection.targetDir} ... ]`) console.log(
await ssh.execCommand(`mkdir ${connection.targetDir}`, { cwd: connection.targetPath }) `[ mv ${connection.targetDir} ${
const toCreate = necessaryPath(path.join('./', wo.envar.deploy.fromPath)) connection.targetDir
for (const name of toCreate) { }-backup-${new Date().toISOString()} ... ]`
console.log(`[ mkdir ${connection.targetDir}/${name.join('/')} ... ]`) )
await ssh.execCommand(`mkdir ${connection.targetDir}/${name.join('/')}`, { cwd: connection.targetPath }) await ssh.execCommand(
} `mv ${connection.targetDir} ${
connection.targetDir
let err }-backup-${new Date().toISOString()}`,
console.log(`[ Upload to ${connection.targetPath}/${connection.targetDir} ... ]`) { cwd: connection.targetPath }
await ssh.putDirectory(path.join('./', wo.envar.deploy.fromPath), `${connection.targetPath}/${connection.targetDir}`, { )
concurrency: 10, console.log(`[ mkdir ${connection.targetDir} ... ]`)
recursive: true, await ssh.execCommand(`mkdir ${connection.targetDir}`, {
validate: itemPath => { cwd: connection.targetPath
const baseName = path.basename(itemPath) })
return !baseName.endsWith('.map') const toCreate = necessaryPath(path.join('./', wo.envar.deploy.fromPath))
}, for (const name of toCreate) {
tick: (fromPath, remotePath, error) => { console.log(`[ mkdir ${connection.targetDir}/${name.join('/')} ... ]`)
console.log(`Uploading "${fromPath}" ===> "${remotePath}" ${error || 'succeeded!'}`) await ssh.execCommand(
err = error `mkdir ${connection.targetDir}/${name.join('/')}`,
}, { cwd: connection.targetPath }
}) )
ssh.dispose()
if (err) {
console.error(`🤷‍♀️🤷‍♀️🤷‍♀️ Failed deploy ${wo.envar.deploy.fromPath} to ${connection.targetPath}/${connection.targetDir} 🤷‍♀️🤷‍♀️🤷‍♀️`)
process.exit(1)
} else {
console.info(`😊😊😊 Successfully deployed [${wo.envar.deploy.fromPath}] to [${connection.targetPath}/${connection.targetDir}] 😊😊😊`)
if (connection.url){
console.info(`😊😊😊 ${connection.url} 😊😊😊`)
} }
process.exit()
} let err
}).catch(err => { console.log(
console.error(err) `[ Upload to ${connection.targetPath}/${connection.targetDir} ... ]`
ssh.dispose() )
console.error(`🤷‍♀️🤷‍♀️🤷‍♀️ Failed deploy [${wo.envar.deploy.fromPath}] to [${connection.targetPath}/${connection.targetDir}] 🤷‍♀️🤷‍♀️🤷‍♀️`) await ssh.putDirectory(
process.exit(1) path.join('./', wo.envar.deploy.fromPath),
}) `${connection.targetPath}/${connection.targetDir}`,
{
concurrency: 10,
recursive: true,
validate: itemPath => {
const baseName = path.basename(itemPath)
return !baseName.endsWith('.map')
},
tick: (fromPath, remotePath, error) => {
console.log(
`Uploading "${fromPath}" ===> "${remotePath}" ${error ||
'succeeded!'}`
)
err = error
}
}
)
ssh.dispose()
if (err) {
console.error(
`🤷‍♀️🤷‍♀️🤷‍♀️ Failed deploy ${wo.envar.deploy.fromPath} to ${connection.targetPath}/${connection.targetDir} 🤷‍♀️🤷‍♀️🤷‍♀️`
)
process.exit(1)
} else {
console.info(
`😊😊😊 Successfully deployed [${wo.envar.deploy.fromPath}] to [${connection.targetPath}/${connection.targetDir}] 😊😊😊`
)
if (connection.url) {
console.info(`😊😊😊 ${connection.url} 😊😊😊`)
}
process.exit()
}
})
.catch(err => {
console.error(err)
ssh.dispose()
console.error(
`🤷‍♀️🤷‍♀️🤷‍♀️ Failed deploy [${wo.envar.deploy.fromPath}] to [${connection.targetPath}/${connection.targetDir}] 🤷‍♀️🤷‍♀️🤷‍♀️`
)
process.exit(1)
})
} }
/** ********************** 连接到 Git主机拷贝文件到指定路径 ************* **/ /** ********************** 连接到 Git主机拷贝文件到指定路径 ************* **/
function deployToGit(connection){ function deployToGit (connection) {
const pathFn = require('path') const pathFn = require('path')
const fs = require('hexo-fs') const fs = require('hexo-fs')
const chalk = require('chalk') const chalk = require('chalk')
@ -192,7 +253,7 @@ function deployToGit(connection){
const spawn = require('hexo-util/lib/spawn') const spawn = require('hexo-util/lib/spawn')
const swigHelpers = { const swigHelpers = {
now: function(format) { now: function (format) {
return moment().format(format) return moment().format(format)
} }
} }
@ -200,7 +261,7 @@ function deployToGit(connection){
const rRepoURL = /^(?:(?:git|https?|git\+https|git\+ssh):\/\/)?(?:[^@]+@)?([^\/]+?)[\/:](.+?)\.git$/ // eslint-disable-line no-useless-escape const rRepoURL = /^(?:(?:git|https?|git\+https|git\+ssh):\/\/)?(?:[^@]+@)?([^\/]+?)[\/:](.+?)\.git$/ // eslint-disable-line no-useless-escape
const rGithubPage = /\.github\.(io|com)$/ const rGithubPage = /\.github\.(io|com)$/
function parseRepo(repo) { function parseRepo (repo) {
const split = repo.split(',') const split = repo.split(',')
const url = split.shift() const url = split.shift()
let branch = split[0] let branch = split[0]
@ -211,7 +272,9 @@ function deployToGit(connection){
const path = match[2] const path = match[2]
if (host === 'github.com') { if (host === 'github.com') {
branch = rGithubPage.test(path) ? (connection.branch || 'main') : 'gh-pages' branch = rGithubPage.test(path)
? connection.branch || 'main'
: 'gh-pages'
} else if (host === 'coding.net') { } else if (host === 'coding.net') {
branch = 'coding-pages' branch = 'coding-pages'
} }
@ -223,7 +286,7 @@ function deployToGit(connection){
} }
} }
function parseConnection(args) { function parseConnection (args) {
const repo = args.repo || args.repository const repo = args.repo || args.repository
if (!repo) throw new TypeError('repo is required!') if (!repo) throw new TypeError('repo is required!')
@ -241,7 +304,7 @@ function deployToGit(connection){
return result return result
} }
function exec() { function exec () {
const targetDir = '' const targetDir = ''
const deployDir = pathFn.join(targetDir, '.deploy_git') const deployDir = pathFn.join(targetDir, '.deploy_git')
const fromDir = wo.envar.deploy.fromPath const fromDir = wo.envar.deploy.fromPath
@ -258,15 +321,17 @@ function deployToGit(connection){
if (!connection.repo && !connection.repository) { if (!connection.repo && !connection.repository) {
let help = '' let help = ''
help += 'You have to configure the deployment settings in config files or command line first!\n\n' help +=
'You have to configure the deployment settings in config files or command line first!\n\n'
help += 'Example:\n' help += 'Example:\n'
help += ' node deploy.js -t git -r https://github.com/OWNER/OWNER.github.io -b main -f ./dist' help +=
' node deploy.js -t git -r https://github.com/OWNER/OWNER.github.io -b main -f ./dist'
console.log(help) console.log(help)
return return
} }
function git(...connection) { function git (...connection) {
return spawn('git', connection, { return spawn('git', connection, {
cwd: deployDir, cwd: deployDir,
verbose: verbose, verbose: verbose,
@ -274,114 +339,146 @@ function deployToGit(connection){
}) })
} }
function setup() { function setup () {
const userName = wo.envar.deploy.gitname || '' const userName = wo.envar.deploy.gitname || ''
const userEmail = wo.envar.deploy.gitemail || '' const userEmail = wo.envar.deploy.gitemail || ''
// Create a placeholder for the first commit // Create a placeholder for the first commit
return fs.writeFile(pathFn.join(deployDir, 'placeholder'), '').then(() => { return fs
return git('init') .writeFile(pathFn.join(deployDir, 'placeholder'), '')
}).then(() => { .then(() => {
return userName && git('config', 'user.name', userName) return git('init')
}).then(() => { })
return userEmail && git('config', 'user.email', userEmail) .then(() => {
}).then(() => { return userName && git('config', 'user.name', userName)
return git('add', '-A') })
}).then(() => { .then(() => {
return git('commit', '-m', 'First commit') return userEmail && git('config', 'user.email', userEmail)
}) })
} .then(() => {
return git('add', '-A')
function push(repo) { })
return git('add', '-A').then(() => { .then(() => {
return git('commit', '-m', message).catch(() => { return git('commit', '-m', 'First commit')
// Do nothing. It's OK if nothing to commit.
}) })
}).then(() => {
return git('push', '-u', repo.url, 'HEAD:' + repo.branch, '--force')
}).then(()=>{
console.info(`😊😊😊 Successfully deployed [${wo.envar.deploy.fromPath}] to [${connection.repo}#${connection.branch}] 😊😊😊`)
if (connection.url){
console.info(`😊😊😊 ${connection.url} 😊😊😊`)
}
}).catch((err)=>{
console.error(`🤷‍♀️🤷‍♀️🤷‍♀️ Failed deploy [${wo.envar.deploy.fromPath}] to [${connection.repo}#${connection.branch}] 🤷‍♀️🤷‍♀️🤷‍♀️`)
process.exit(1)
})
} }
return fs.exists(deployDir).then(function(exist) { function push (repo) {
if (exist) return return git('add', '-A')
.then(() => {
return git('commit', '-m', message).catch(() => {
// Do nothing. It's OK if nothing to commit.
})
})
.then(() => {
return git('push', '-u', repo.url, 'HEAD:' + repo.branch, '--force')
})
.then(() => {
console.info(
`😊😊😊 Successfully deployed [${wo.envar.deploy.fromPath}] to [${connection.repo}#${connection.branch}] 😊😊😊`
)
if (connection.url) {
console.info(`😊😊😊 ${connection.url} 😊😊😊`)
}
})
.catch(err => {
console.error(
`🤷‍♀️🤷‍♀️🤷‍♀️ Failed deploy [${wo.envar.deploy.fromPath}] to [${connection.repo}#${connection.branch}] 🤷‍♀️🤷‍♀️🤷‍♀️`
)
process.exit(1)
})
}
// log.info('Setting up Git deployment...') return fs
return setup() .exists(deployDir)
}).then(() => { .then(function (exist) {
// log.info('Clearing .deploy_git folder...') if (exist) return
return fs.emptyDir(deployDir)
}).then(() => {
const opts = {}
// log.info('Copying files from local folder...')
if (typeof ignoreHidden === 'object') {
opts.ignoreHidden = ignoreHidden.public
} else {
opts.ignoreHidden = ignoreHidden
}
if (typeof ignorePattern === 'string') { // log.info('Setting up Git deployment...')
opts.ignorePattern = new RegExp(ignorePattern) return setup()
} else if (typeof ignorePattern === 'object' && Reflect.apply(Object.prototype.hasOwnProperty, ignorePattern, ['public'])) { })
opts.ignorePattern = new RegExp(ignorePattern.public) .then(() => {
} // log.info('Clearing .deploy_git folder...')
return fs.emptyDir(deployDir)
return fs.copyDir(fromDir, deployDir, opts) })
}).then(() => { .then(() => {
// log.info('Copying files from extend dirs...')
if (!extendDirs) {
return
}
if (typeof extendDirs === 'string') {
extendDirs = [extendDirs]
}
const mapFn = function(dir) {
const opts = {} const opts = {}
const extendPath = pathFn.join(targetDir, dir) // log.info('Copying files from local folder...')
const extendDist = pathFn.join(deployDir, dir)
if (typeof ignoreHidden === 'object') { if (typeof ignoreHidden === 'object') {
opts.ignoreHidden = ignoreHidden[dir] opts.ignoreHidden = ignoreHidden.public
} else { } else {
opts.ignoreHidden = ignoreHidden opts.ignoreHidden = ignoreHidden
} }
if (typeof ignorePattern === 'string') { if (typeof ignorePattern === 'string') {
opts.ignorePattern = new RegExp(ignorePattern) opts.ignorePattern = new RegExp(ignorePattern)
} else if (typeof ignorePattern === 'object' && Reflect.apply(Object.prototype.hasOwnProperty, ignorePattern, [dir])) { } else if (
opts.ignorePattern = new RegExp(ignorePattern[dir]) typeof ignorePattern === 'object' &&
Reflect.apply(Object.prototype.hasOwnProperty, ignorePattern, [
'public'
])
) {
opts.ignorePattern = new RegExp(ignorePattern.public)
} }
return fs.copyDir(extendPath, extendDist, opts) return fs.copyDir(fromDir, deployDir, opts)
} })
.then(() => {
return Promise.map(extendDirs, mapFn, { // log.info('Copying files from extend dirs...')
concurrency: 2
if (!extendDirs) {
return
}
if (typeof extendDirs === 'string') {
extendDirs = [extendDirs]
}
const mapFn = function (dir) {
const opts = {}
const extendPath = pathFn.join(targetDir, dir)
const extendDist = pathFn.join(deployDir, dir)
if (typeof ignoreHidden === 'object') {
opts.ignoreHidden = ignoreHidden[dir]
} else {
opts.ignoreHidden = ignoreHidden
}
if (typeof ignorePattern === 'string') {
opts.ignorePattern = new RegExp(ignorePattern)
} else if (
typeof ignorePattern === 'object' &&
Reflect.apply(Object.prototype.hasOwnProperty, ignorePattern, [dir])
) {
opts.ignorePattern = new RegExp(ignorePattern[dir])
}
return fs.copyDir(extendPath, extendDist, opts)
}
return Promise.map(extendDirs, mapFn, {
concurrency: 2
})
})
.then(() => {
return parseConnection(connection)
})
.each(function (repo) {
console.log('########## repo ###########')
console.log(repo)
return push(repo)
}) })
}).then(() => {
return parseConnection(connection)
}).each(function(repo) {
console.log('########## repo ###########')
console.log(repo)
return push(repo)
})
} // end of function exec } // end of function exec
function commitMessage(connection) { function commitMessage (connection) {
const message = connection.m || connection.msg || connection.message || 'Site updated: {{ now(\'YYYY-MM-DD HH:mm:ss\') }}' const message =
connection.m ||
connection.msg ||
connection.message ||
"Site updated: {{ now('YYYY-MM-DD HH:mm:ss') }}"
return swig.compile(message)(swigHelpers) return swig.compile(message)(swigHelpers)
} }
exec() exec()
} }