diff --git a/deploy.js b/deploy.js index 17a1afe..5654e03 100644 --- a/deploy.js +++ b/deploy.js @@ -9,10 +9,10 @@ const deepmerge = require('deepmerge') const wo = (global.wo = { envar: { deploy: { - fromPath: './webroot', + fromPath: './webroot', gotoTarget: 'github', - - vultr: { + + vultr: { targetType: 'ssh', host: undefined, port: 22, @@ -20,7 +20,7 @@ const wo = (global.wo = { targetDir: 'webroot', user: undefined, password: undefined, - key: `${process.env.HOME}/.ssh/id_rsa`, + key: `${process.env.HOME}/.ssh/id_rsa` }, github: { targetType: 'git', @@ -30,7 +30,7 @@ const wo = (global.wo = { gitemail: undefined, user: 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 { 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)) - 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)) - console.info(`${configFile} loaded`) + console.info(`- ${configFile} loaded`) } } 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 IP or domain name of the target server.`) .option('-P, --port ', `Ssh port number of the target server.`) - .option('-b, --targetPath ', `Destination path to deploy on the target.`) - .option('-d, --targetDir ', `Destination folder to deploy on the target.`) + .option( + '-d, --targetPath ', + `Destination path to deploy on the target.` + ) + .option( + '-D, --targetDir ', + `Destination folder to deploy on the target.` + ) .option('-r, --repo ', `git repo address.`) .option('-b, --branch ', `git repo branch.`) @@ -70,12 +84,19 @@ commander .option('-m, --gitemail ', `git user email.`) .option('-u, --user ', `User id to login the target server.`) - .option('-k, --key ', `User private key file to login the target server.`) - .option('-p, --password ', `User password to login the target server. You may have to enclose it in "".`) + .option( + '-k, --key ', + `User private key file to login the target server.` + ) + .option( + '-p, --password ', + `User password to login the target server. You may have to enclose it in "".` + ) .parse(process.argv) 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 = { @@ -93,27 +114,35 @@ const connection = { gitemail: commander.gitemail || wo.envar.deploy.connection.gitemail, // common 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, tryKeyboard: true, - onKeyboardInteractive: (name, instructions, lang, prompts, finish) => { // 不起作用 - if (prompts.length > 0 && prompts[0].prompt.toLowerCase().includes('password')) { + onKeyboardInteractive: (name, instructions, lang, prompts, finish) => { + // 不起作用 + if ( + prompts.length > 0 && + prompts[0].prompt.toLowerCase().includes('password') + ) { finish([password]) } }, 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) -}else if (connection.targetType==='git'){ +} else if (connection.targetType === 'git') { deployToGit(connection) } /** ********************** 连接到 Ssh主机,拷贝文件到指定路径 ************* **/ -function deployToSsh(connection){ +function deployToSsh (connection) { const ssh = new (require('node-ssh'))() function subDirs (path) { @@ -129,60 +158,92 @@ function deployToSsh(connection){ }) return dirs } - - const necessaryPath = (path) => { + + 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 ${connection.targetDir} ${connection.targetDir}-backup-${new Date().toISOString()} ... ]`) - await ssh.execCommand(`mv ${connection.targetDir} ${connection.targetDir}-backup-${new Date().toISOString()}`, { cwd: connection.targetPath }) - console.log(`[ mkdir ${connection.targetDir} ... ]`) - await ssh.execCommand(`mkdir ${connection.targetDir}`, { cwd: connection.targetPath }) - const toCreate = necessaryPath(path.join('./', wo.envar.deploy.fromPath)) - for (const name of toCreate) { - console.log(`[ mkdir ${connection.targetDir}/${name.join('/')} ... ]`) - await ssh.execCommand(`mkdir ${connection.targetDir}/${name.join('/')}`, { cwd: connection.targetPath }) - } - - let err - console.log(`[ Upload to ${connection.targetPath}/${connection.targetDir} ... ]`) - await ssh.putDirectory(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} 😊😊😊`) + + ssh + .connect(connection) + .then(async () => { + console.log( + `[ mv ${connection.targetDir} ${ + connection.targetDir + }-backup-${new Date().toISOString()} ... ]` + ) + await ssh.execCommand( + `mv ${connection.targetDir} ${ + connection.targetDir + }-backup-${new Date().toISOString()}`, + { cwd: connection.targetPath } + ) + console.log(`[ mkdir ${connection.targetDir} ... ]`) + await ssh.execCommand(`mkdir ${connection.targetDir}`, { + cwd: connection.targetPath + }) + const toCreate = necessaryPath(path.join('./', wo.envar.deploy.fromPath)) + for (const name of toCreate) { + console.log(`[ mkdir ${connection.targetDir}/${name.join('/')} ... ]`) + await ssh.execCommand( + `mkdir ${connection.targetDir}/${name.join('/')}`, + { cwd: connection.targetPath } + ) } - 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) - }) + + let err + console.log( + `[ Upload to ${connection.targetPath}/${connection.targetDir} ... ]` + ) + await ssh.putDirectory( + 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主机,拷贝文件到指定路径 ************* **/ -function deployToGit(connection){ +function deployToGit (connection) { const pathFn = require('path') const fs = require('hexo-fs') const chalk = require('chalk') @@ -192,7 +253,7 @@ function deployToGit(connection){ const spawn = require('hexo-util/lib/spawn') const swigHelpers = { - now: function(format) { + now: function (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 rGithubPage = /\.github\.(io|com)$/ - function parseRepo(repo) { + function parseRepo (repo) { const split = repo.split(',') const url = split.shift() let branch = split[0] @@ -211,7 +272,9 @@ function deployToGit(connection){ const path = match[2] 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') { branch = 'coding-pages' } @@ -223,25 +286,25 @@ function deployToGit(connection){ } } - function parseConnection(args) { + function parseConnection (args) { const repo = args.repo || args.repository if (!repo) throw new TypeError('repo is required!') - + if (typeof repo === 'string') { const data = parseRepo(repo) data.branch = args.branch || data.branch - + return [data] } - + const result = Object.keys(repo).map(key => { return parseRepo(repo[key]) }) - + return result } - function exec() { + function exec () { const targetDir = '' const deployDir = pathFn.join(targetDir, '.deploy_git') const fromDir = wo.envar.deploy.fromPath @@ -258,15 +321,17 @@ function deployToGit(connection){ if (!connection.repo && !connection.repository) { 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 += ' 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) return } - function git(...connection) { + function git (...connection) { return spawn('git', connection, { cwd: deployDir, verbose: verbose, @@ -274,114 +339,146 @@ function deployToGit(connection){ }) } - function setup() { + function setup () { const userName = wo.envar.deploy.gitname || '' const userEmail = wo.envar.deploy.gitemail || '' // Create a placeholder for the first commit - return fs.writeFile(pathFn.join(deployDir, 'placeholder'), '').then(() => { - return git('init') - }).then(() => { - return userName && git('config', 'user.name', userName) - }).then(() => { - return userEmail && git('config', 'user.email', userEmail) - }).then(() => { - return git('add', '-A') - }).then(() => { - return git('commit', '-m', 'First commit') - }) - } - - function push(repo) { - return git('add', '-A').then(() => { - return git('commit', '-m', message).catch(() => { - // Do nothing. It's OK if nothing to commit. + return fs + .writeFile(pathFn.join(deployDir, 'placeholder'), '') + .then(() => { + return git('init') + }) + .then(() => { + return userName && git('config', 'user.name', userName) + }) + .then(() => { + return userEmail && git('config', 'user.email', userEmail) + }) + .then(() => { + return git('add', '-A') + }) + .then(() => { + return git('commit', '-m', 'First 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) { - if (exist) return + function push (repo) { + 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 setup() - }).then(() => { - // log.info('Clearing .deploy_git folder...') - 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 - } + return fs + .exists(deployDir) + .then(function (exist) { + if (exist) return - if (typeof ignorePattern === 'string') { - opts.ignorePattern = new RegExp(ignorePattern) - } else if (typeof ignorePattern === 'object' && Reflect.apply(Object.prototype.hasOwnProperty, ignorePattern, ['public'])) { - opts.ignorePattern = new RegExp(ignorePattern.public) - } - - return fs.copyDir(fromDir, deployDir, opts) - }).then(() => { - // log.info('Copying files from extend dirs...') - - if (!extendDirs) { - return - } - - if (typeof extendDirs === 'string') { - extendDirs = [extendDirs] - } - - const mapFn = function(dir) { + // log.info('Setting up Git deployment...') + return setup() + }) + .then(() => { + // log.info('Clearing .deploy_git folder...') + return fs.emptyDir(deployDir) + }) + .then(() => { const opts = {} - const extendPath = pathFn.join(targetDir, dir) - const extendDist = pathFn.join(deployDir, dir) - + // log.info('Copying files from local folder...') if (typeof ignoreHidden === 'object') { - opts.ignoreHidden = ignoreHidden[dir] + opts.ignoreHidden = ignoreHidden.public } 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]) + } else if ( + typeof ignorePattern === 'object' && + Reflect.apply(Object.prototype.hasOwnProperty, ignorePattern, [ + 'public' + ]) + ) { + opts.ignorePattern = new RegExp(ignorePattern.public) } - return fs.copyDir(extendPath, extendDist, opts) - } - - return Promise.map(extendDirs, mapFn, { - concurrency: 2 + return fs.copyDir(fromDir, deployDir, opts) + }) + .then(() => { + // log.info('Copying files from extend dirs...') + + 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 - function commitMessage(connection) { - const message = connection.m || connection.msg || connection.message || 'Site updated: {{ now(\'YYYY-MM-DD HH:mm:ss\') }}' + function commitMessage (connection) { + const message = + connection.m || + connection.msg || + connection.message || + "Site updated: {{ now('YYYY-MM-DD HH:mm:ss') }}" return swig.compile(message)(swigHelpers) } exec() - -} \ No newline at end of file +}