连接服务器工具
MobaXterm
securecrt
xshell
putty
powershell
第一种方式
借助ssh这些插件链接服务器部署,具体代码如下
// deploy/index.js
import scpClient from 'scp2'
import chalk from 'chalk'
import ssh from 'ssh2'
import ora from 'ora'
import path from 'path'
import server from './config.js'
const source_path = path.resolve() + '/dist'
const spinner = ora('正在发布到测试服务器...')
spinner.start()
const Client = ssh.Client
const conn = new Client()
conn.on('ready', function () {
conn.exec('rm -rf /www/wwwroot/***(项目放置静态地址)', function (err, stream) {
if (err) throw err
stream.on('close', function () {
// 在执行shell命令后,把开始上传部署项目代码放到这里面
scpClient.scp(source_path, {
host: server.host,
port: server.port,
username: server.username,
password: server.password,
path: server.path, // 项目放置静态地址(服务器中地址)
}, function (err) {
spinner.stop()
if (err) {
console.log(chalk.red('发布失败.\n'))
} else {
console.log(chalk.green('Success! 成功发布到服务器! \n'))
}
})
conn.end()
}).on('data', function (data) {
console.log('STDOUT: ' + data)
}).stderr.on('data', function (data) {
console.log('STDERR: ' + data)
})
})
}).connect({
host: server.host,
port: server.port,
username: server.username,
password: server.password,
})
// deploy.config.js
export default {
host: '',
port: '',
username: '',
password: '',
}
// package.json
{
"type": "module",
"scripts": {
"build:develop": "vite build --mode develop && node ./deploy/index.js",
}
}
第二种
全局安装gupack部署
/build/gupack-config.js
'use strict';
const path = require('path')
const argv = require('yargs').argv;
const dist = argv['d'] || argv['dest'] || '../dist';
const distPath = path.isAbsolute(dist) ? dist : path.resolve(__dirname, dist);
//导出模块
module.exports = {
sourceDir: path.resolve(__dirname, '../src'),
buildDir: distPath,
// 每次执行编译之前是否清理当前编译目录
startClean: true,
watch: false,
// task任务列表
buildTasks: require('./tasks'),
// 部署配置
deploy: require('./deploy'),
};
/build/deploy/index.js
/**
* Deploy 应用部署相关, 可根据env设置对应的节点,支持多节点部署
*/
'use strict';
const path = require('path');
console.log('110', process.env)
const filters = [
path.resolve(__dirname,`../../dist/static/**/*`),
];
const env = process.env.NODE_ENV;
const servers = {
sit: [
// {
// host: '10.91.193.190',
// user: '*',
// pass: '*',
// remotePath: '/*',
// type: 'increment',
// filters
// }
],
uat: [],
prod: [],
};
module.exports = servers[env];
/build/tasks/index.js
/**
* Build Tasks 应用编译相关任务
*/
'use strict';
// const path = require('path');
// const $ = require('gulp-load-plugins')();
// const config = require('../../config');
// const { createVersion, getVersion } = require('../webpack/utils');
// const distPath = config.dest.path;
// const sourcePath = config.source.path;
// createVersion();
module.exports = {
// ---说明:单个任务配置
// webpack编译
// 'build.main': $.shell.task([
// `cross-env NODE_ENV=${config.env} node ${path.resolve(__dirname, '../webpack/build.js')}`,
// ]),
// 合并配置文件
// 'build.concat:config': {
// src: [`${sourcePath}/config/env/${config.env}.js`, `${sourcePath}/config/init.js`],
// dest: `${distPath}/assets/js`,
// loader: stream => stream.pipe($.concat('index.js')),
// },
// 处理manifest
// 'replace.manifest.js': {
// src: `${distPath}/assets/js/manifest.js`,
// dest: `${distPath}/assets/js`,
// rely: ['build.main'],
// loader: stream => stream.pipe($.replace(/\".js\";/g, `".js?_vc=${Date.now()}";`)),
// // loader: stream => stream.pipe($.replace(/\".js\";/g, `".js?_vc=${getVersion()}";`)),
// },
};
package.json
"sit-gupack": "cross-env NODE_ENV=sit gupack build -f ./build/gupack-config.js",
"uat-gupack": "cross-env NODE_ENV=uat gupack build -f ./build/gupack-config.js",
第三种
/build/deploy/indexjs
// deploy.js
const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');
const node_ssh = require('node-ssh');
const archiver = require('archiver');
const {
successLog, errorLog, underlineLog } = require('../utils/index');
const projectDir = process.cwd();
let ssh = new node_ssh(); // 生成ssh实例
// 部署流程入口
function deploy(config) {
const {
script } = config;
try {
console.log(`\n(1)${
script}`);
childProcess.execSync(`${
script}`);
successLog(' 打包成功');
startZip(config);
} catch (err) {
errorLog(err);
process.exit(1);
}
}
// 开始打包
function startZip(config) {
let {
distPath, host } = config;
distPath = path.resolve(projectDir, distPath);
console.log('(2)打包成zip');
const archive = archiver('zip', {
zlib: {
level: 9 },
}).on('error', err => {
throw err;
});
const output = fs.createWriteStream(`${
projectDir}/dist.zip`).on('close', err => {
if (err) {
console.log(' 关闭archiver异常:', err);
return;
}
successLog(' zip打包成功');
console.log(`(3)连接${
underlineLog(host)}`);
uploadFile(config);
});
archive.pipe(output);
archive.directory(distPath, '/');
archive.finalize();
}
// 上传文件
function uploadFile(config) {
const {
host, port, username, password, privateKey, passphrase, } = config;
const sshConfig = {
host,
port,
username,
password,
privateKey,
passphrase
};
ssh.connect(sshConfig)
.then(() => {
successLog(` SSH连接成功`);
console.log(`(4)上传zip至目录${
underlineLog(config.webDir)}`);
ssh.putFile(`${
projectDir}/dist.zip`, `${
config.webDir}/dist.zip`)
.then(() => {
successLog(` zip包上传成功`);
console.log('(5)解压zip包');
statrRemoteShell(config);
})
.catch(err => {
errorLog(' 文件传输异常', err);
process.exit(0);
});
})
.catch(err => {
errorLog(' 连接失败', err);
process.exit(0);
});
}
// 执行Linux命令
function runCommand(command, webDir) {
return new Promise((resolve, reject) => {
ssh.execCommand(command, {
cwd: webDir })
.then(result => {
resolve();
// if (result.stdout) {
// successLog(result.stdout);
// }
if (result.stderr) {
errorLog(result.stderr);
process.exit(1);
}
})
.catch(err => {
reject(err);
});
});
}
// 开始执行远程命令
function statrRemoteShell(config) {
const {
webDir } = config;
const commands = [`cd ${
webDir}`, 'pwd', 'unzip -o dist.zip && rm -f dist.zip'];
const promises = [];
for (let i = 0; i < commands.length; i += 1) {
promises.push(runCommand(commands[i], webDir));
}
Promise.all(promises)
.then(() => {
successLog(' 解压成功');
console.log('(6)开始删除本地dist.zip');
deleteLocalZip(config);
})
.catch(err => {
errorLog(' 文件解压失败', err);
process.exit(0);
});
}
// 删除本地dist.zip包
function deleteLocalZip(config) {
const {
projectName, name } = config;
fs.unlink(`${
projectDir}/dist.zip`, err => {
if (err) {
errorLog(' 本地dist.zip删除失败', err);
}
successLog(' 本地dist.zip删除成功\n');
successLog(`\n 恭喜您,${
underlineLog(projectName)}项目${
underlineLog(name)}部署成功了^_^\n`);
process.exit(0);
});
}
module.exports = deploy;
/build/deploy-cli/deploy.config.js
// deploy.config.js
module.exports = {
privateKey: '', // 本地私钥地址,位置一般在C:/Users/xxx/.ssh/id_rsa,非必填,有私钥则配置
passphrase: '', // 本地私钥密码,非必填,有私钥则配置
projectName: '', // 项目名称
dev: {
// 测试环境
name: '测试环境',
script: "npm run build", // 测试环境打包脚本
host: '', // 测试服务器地址
port: 22, // ssh port,一般默认22
username: '', // 登录服务器用户名
password: '', // 登录服务器密码
distPath: 'dist', // 本地打包dist目录
webDir: '', // // 测试环境服务器地址
},
prod: {
// 线上环境
name: '线上环境',
script: "npm run build", // 线上环境打包脚本
host: '', // 线上服务器地址
port: 22, // ssh port,一般默认22
username: '', // 登录服务器用户名
password: '', // 登录服务器密码
distPath: 'dist', // 本地打包dist目录
webDir: '' // 线上环境web目录
}
// 再还有多余的环境按照这个格式写即可
}
/build/deploy-cli/index.js
// deploy.js
const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');
const ora = require('ora');
const node_ssh = require('node-ssh');
const archiver = require('archiver');
const {
successLog, errorLog, underlineLog } = require('../utils/index');
const projectDir = process.cwd();
let ssh = new node_ssh(); // 生成ssh实例
// 部署流程入口
async function deploy(config) {
const {
script, webDir, distPath, projectName, name } = config;
try {
execBuild(script);
await startZip(distPath);
await connectSSH(config);
await uploadFile(webDir);
await unzipFile(webDir);
await deleteLocalZip();
successLog(`\n 恭喜您,${
underlineLog(projectName)}项目${
underlineLog(name)}部署成功了^_^\n`);
process.exit(0);
} catch (err) {
errorLog(` 部署失败 ${
err}`);
process.exit(1);
}
}
// 第一步,执行打包脚本
function execBuild(script) {
try {
console.log(`\n(1)${
script}`);
const spinner = ora('正在打包中');
spinner.start();
console.log();
childProcess.execSync(script, {
cwd: projectDir });
spinner.stop();
successLog(' 打包成功');
} catch (err) {
errorLog(err);
process.exit(1);
}
}
// 第二部,打包zip
function startZip(distPath) {
return new Promise((resolve, reject) => {
distPath = path.resolve(projectDir, distPath);
console.log('(2)打包成zip');
const archive = archiver('zip', {
zlib: {
level: 9 },
}).on('error', err => {
throw err;
});
const output = fs.createWriteStream(`${
projectDir}/dist.zip`);
output.on('close', err => {
if (err) {
errorLog(` 关闭archiver异常 ${
err}`);
reject(err);
process.exit(1);
}
successLog(' zip打包成功');
resolve();
});
archive.pipe(output);
archive.directory(distPath, '/');
archive.finalize();
});
}
// 第三步,连接SSH
async function connectSSH(config) {
const {
host, port, username, password, privateKey, passphrase, distPath } = config;
const sshConfig = {
host,
port,
username,
password,
privateKey,
passphrase
};
try {
console.log(`(3)连接${
underlineLog(host)}`);
await ssh.connect(sshConfig);
successLog(' SSH连接成功');
} catch (err) {
errorLog(` 连接失败 ${
err}`);
process.exit(1);
}
}
// 第四部,上传zip包
async function uploadFile(webDir) {
try {
console.log(`(4)上传zip至目录${
underlineLog(webDir)}`);
await ssh.putFile(`${
projectDir}/dist.zip`, `${
webDir}/dist.zip`);
successLog(' zip包上传成功');
} catch (err) {
errorLog(` zip包上传失败 ${
err}`);
process.exit(1);
}
}
// 运行命令
async function runCommand(command, webDir) {
await ssh.execCommand(command, {
cwd: webDir });
}
// 第五步,解压zip包
async function unzipFile(webDir) {
try {
console.log('(5)开始解压zip包');
await runCommand(`cd ${
webDir}`, webDir);
await runCommand('unzip -o dist.zip && rm -f dist.zip', webDir);
successLog(' zip包解压成功');
} catch (err) {
errorLog(` zip包解压失败 ${
err}`);
process.exit(1);
}
}
// 第六步,删除本地dist.zip包
async function deleteLocalZip() {
return new Promise((resolve, reject) => {
console.log('(6)开始删除本地zip包');
fs.unlink(`${
projectDir}/dist.zip`, err => {
if (err) {
errorLog(` 本地zip包删除失败 ${
err}`, err);
reject(err);
process.exit(1);
}
successLog(' 本地dist.zip删除成功\n');
resolve();
});
});
}
module.exports = deploy;
/build/deploy-cli/init.js
// init.js
// #!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const download = require('download-git-repo');
const ora = require('ora');
const {
successLog, infoLog, errorLog } = require('../utils/index');
let tmp = 'deploy';
const deployPath = path.join(process.cwd(), './deploy');
const deployConfigPath = `${
deployPath}/deploy.config.js`;
const deployGit = 'dadaiwei/fe-deploy-cli-template';
// 检查部署目录及部署配置文件是否存在
const checkDeployExists = () => {
if (fs.existsSync(deployPath) && fs.existsSync(deployConfigPath)) {
infoLog('deploy目录下的deploy.config.js配置文件已经存在,请勿重新下载');
process.exit(1);
return;
}
downloadAndGenerate(deployGit);
};
// 下载部署脚本配置
const downloadAndGenerate = templateUrl => {
const spinner = ora('开始生成部署模板');
spinner.start();
download(templateUrl, tmp, {
clone: false }, err => {
if (err) {
console.log();
errorLog(err);
process.exit(1);
}
spinner.stop();
successLog('模板下载成功,模板位置:deploy/deploy.config.js');
infoLog('请配置deploy目录下的deploy.config.js配置文件');
process.exit(0);
});
};
module.exports = () => {
checkDeployExists();
};
/build/deploy-cli/remore.md
与脚手架相关的npm包:
commander:node.js命令行界面的完整解决方案
download-git-repo:git仓库代码下载
ora:显示加载中的效果
inquirer:用户与命令交互的工具
child_process:npm内置模块,用于执行package.json中的打包script
引用限制
package.json设置
{
...
"type": "module"
"scripts": {
...}
}