billd-deploy
Version:
1,172 lines (1,154 loc) • 41 kB
JavaScript
import { execSync, exec } from 'child_process';
import chalk from 'chalk';
import { cdn } from 'tencentcloud-sdk-nodejs-cdn';
import fs from 'fs';
import path from 'path';
import OSS from 'ali-oss';
import OBS from 'esdk-obs-nodejs';
import qiniu from 'qiniu';
import COS from 'cos-nodejs-sdk-v5';
import { NodeSSH } from 'node-ssh';
import { logData } from 'billd-html-webpack-plugin';
const cache = {
build: "ok",
ssh: "ok",
cos: "ok",
cdn: "ok"
};
const chalkINFO = (v) => {
const time = new Date().toLocaleString("zh-CN");
const prefix = `[${time}] INFO `;
return `${chalk.bgBlueBright.black(prefix)} ${chalk.blueBright(v)}`;
};
const chalkSUCCESS = (v) => {
const time = new Date().toLocaleString("zh-CN");
const prefix = `[${time}] SUCCESS `;
return `${chalk.bgGreenBright.black(prefix)} ${chalk.greenBright(v)}`;
};
const chalkERROR = (v) => {
const time = new Date().toLocaleString("zh-CN");
const prefix = `[${time}] ERROR `;
return `${chalk.bgRedBright.black(prefix)} ${chalk.redBright(v)}`;
};
const chalkWARN = (v) => {
const time = new Date().toLocaleString("zh-CN");
const prefix = `[${time}] WARN `;
return `${chalk.bgHex("#FFA500").black(`${prefix}`)} ${chalk.hex("#FFA500")(
v
)}`;
};
const handleBuild = (buildCmd = "") => {
if (buildCmd === "") {
console.log(chalkERROR(`buildCmd\u4E3A\u7A7A\uFF01`));
return;
}
try {
execSync(buildCmd, {
stdio: "inherit",
cwd: process.cwd()
});
console.log(chalkSUCCESS(`\u6784\u5EFA\u5B8C\u6210`));
} catch (error) {
console.log(chalkERROR(`\u6784\u5EFA\u5931\u8D25`));
console.log(error);
cache.build = "error";
}
};
const handleTencentCdn = async function(data) {
const { tencentCdnConfig: cdnConfig, tencentCdnJob: cdnJob } = data.config;
if (!cdnConfig || !cdnJob) {
console.log(chalkERROR(`CDN\u914D\u7F6E\u9519\u8BEF\uFF01`));
return;
}
const { SecretId, SecretKey } = cdnConfig(data);
const { Purge } = cdnJob(data);
try {
const CdnClient = cdn.v20180606.Client;
const client = new CdnClient({
// 为了保护密钥安全,建议将密钥设置在环境变量中或者配置文件中,请参考本文凭证管理章节。
// 硬编码密钥到代码中有可能随代码泄露而暴露,有安全隐患,并不推荐。
credential: {
secretId: SecretId,
secretKey: SecretKey
}
});
if (Purge.urls) {
let errmsg = "";
try {
await client.PurgeUrlsCache(Purge.urls, (err) => {
if (err) {
errmsg = err;
}
});
console.log(chalkSUCCESS(`\u63D0\u4EA4URL\u5237\u65B0\u6210\u529F\uFF1A${Purge.urls.Urls.join()}`));
} catch (error) {
console.error(error);
errmsg = error;
}
if (errmsg !== "") {
console.log(chalkERROR("\u63D0\u4EA4URL\u5237\u65B0\u5931\u8D25"));
console.log(chalkERROR(errmsg));
}
}
if (Purge.paths) {
let errmsg = "";
try {
await client.PurgePathCache(Purge.paths, (err) => {
if (err) {
errmsg = err;
}
});
console.log(
chalkSUCCESS(`\u63D0\u4EA4\u76EE\u5F55\u5237\u65B0\u6210\u529F\uFF1A${Purge.paths.Paths.join()}`)
);
} catch (error) {
console.error(error);
errmsg = error;
}
if (errmsg !== "") {
console.log(chalkERROR("\u63D0\u4EA4\u76EE\u5F55\u5237\u65B0\u5931\u8D25"));
console.log(chalkERROR(errmsg));
}
}
} catch (err) {
console.log(err);
return { err };
}
};
class ConcurrentPoll {
tasks = [];
max = 0;
total = 0;
done;
constructor({ max = 5, done }) {
this.tasks = [];
this.max = max;
this.total = 0;
this.done = done;
setTimeout(() => {
this.run();
}, 0);
}
addTask(task) {
this.tasks.push(task);
this.total += 1;
}
run() {
if (this.tasks.length === 0) {
return Promise.resolve("");
}
const min = Math.min(this.tasks.length, this.max);
for (let i = 0; i < min; i += 1) {
this.max -= 1;
const task = this.tasks.shift();
task().then(() => {
}).catch((err) => {
console.log(err);
}).finally(() => {
this.max += 1;
this.total -= 1;
this.run();
if (this.total === 0) {
this.done?.();
}
});
}
}
}
const handleAliOss = function(data) {
const { aliOssConfig: cosConfig, aliOssFileConfig: cosFileConfig } = data.config;
if (!cosConfig || !cosFileConfig) {
console.log(chalkERROR(`\u963F\u91CC\u4E91oss\u914D\u7F6E\u9519\u8BEF\uFF01`));
return;
}
const aliOssConfig = cosConfig(data);
const aliOssFileConfig = cosFileConfig(data);
function findFile(inputDir) {
const res = [];
function loop(dirArr) {
for (let i = 0; i < dirArr.length; i += 1) {
const file = dirArr[i];
const filePath = path.resolve(inputDir, file);
const stat = fs.statSync(filePath);
const isDir = stat.isDirectory();
if (!isDir) {
res.push(filePath);
} else {
loop(fs.readdirSync(filePath).map((key) => path.join(file, key)));
}
}
}
let inputDirArr = [];
try {
inputDirArr = fs.readdirSync(inputDir);
} catch (error) {
console.log(chalkERROR(`${inputDir},\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF01`));
throw new Error(`${inputDir},\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF01`);
}
loop(inputDirArr);
return res;
}
try {
const uploadOkRecord = /* @__PURE__ */ new Map();
const uploadErrRecord = /* @__PURE__ */ new Map();
const allFile = [];
const client = new OSS({
// yourregion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
region: aliOssConfig.region,
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
accessKeyId: aliOssConfig.accessKeyId,
accessKeySecret: aliOssConfig.accessKeySecret,
// 填写Bucket名称。
bucket: aliOssConfig.bucket,
// 当前的oss前缀
prefix: aliOssConfig.prefix
});
if (aliOssFileConfig.dir) {
allFile.push(...findFile(aliOssFileConfig.dir.local));
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6E\u4E0A\u4F20\u672C\u5730\u76EE\u5F55\u5230ali-oss\u76EE\u5F55"));
}
if (aliOssFileConfig.file) {
aliOssFileConfig.file.local.forEach((item) => {
allFile.push(item);
});
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6E\u4E0A\u4F20\u672C\u5730\u6587\u4EF6\u5230ali-oss\u76EE\u5F55"));
}
async function put(ossFlieName, filePath) {
try {
const result = await client.put(
ossFlieName,
filePath,
// 自定义headers
{
headers: {
// 指定PutObject操作时是否覆盖同名目标Object。此处设置为true,表示禁止覆盖同名Object。设置为false,表示允许覆盖同名Object。
"x-oss-forbid-overwrite": "false"
}
}
);
const status = result.res.status;
if (status === 200) {
uploadOkRecord.set(filePath, status);
console.log(
chalkSUCCESS(
`\u4E0A\u4F20ali-oss\u6210\u529F(${uploadOkRecord.size}/${allFile.length}): ${filePath} ===> ${ossFlieName}`
)
);
} else {
uploadErrRecord.set(filePath, status);
console.log(result);
cache.cos = "error";
console.log(
chalkERROR(
// eslint-disable-next-line
`\u4E0A\u4F20ali-oss\u5931\u8D25(${uploadErrRecord.size}/${allFile.length}): ${filePath} ===> ${ossFlieName}`
)
);
}
const progress = uploadOkRecord.size + uploadErrRecord.size;
if (progress === allFile.length) {
console.log(
chalkINFO(
`\u6240\u6709\u6587\u4EF6\u4E0A\u4F20ali-oss\u5B8C\u6210\u3002\u6210\u529F\uFF1A${uploadOkRecord.size}/${allFile.length}\uFF1B\u5931\u8D25\uFF1A${uploadErrRecord.size}/${allFile.length}`
)
);
if (uploadErrRecord.size) {
cache.cos = "error";
console.log(chalkERROR(`\u4E0A\u4F20ali-oss\u5931\u8D25\u6570\u636E`), uploadErrRecord);
}
}
} catch (error) {
cache.cos = "error";
console.log(chalkERROR(`\u4E0A\u4F20ali-oss\u9519\u8BEF`), error);
}
}
return new Promise((resolve) => {
const uploadQueue = new ConcurrentPoll({
max: 5,
done: () => resolve("all done~")
});
allFile.forEach((filePath) => {
if (aliOssFileConfig.file) {
if (aliOssFileConfig.file.local.includes(filePath)) {
const filename = filePath.split(path.sep).pop() || "";
const ossFlieName = path.join(aliOssConfig.prefix, filename);
uploadQueue.addTask(
() => put(
path.sep === "/" ? ossFlieName : ossFlieName.replace(/\\/g, "/"),
filePath
)
);
} else {
if (aliOssFileConfig.dir) {
const dirName = aliOssFileConfig.dir.local.split(path.sep).pop() || "";
const ignoreDir = aliOssFileConfig.dir.ignoreDir;
const ossFlieName = aliOssConfig.prefix + filePath.replace(
aliOssFileConfig.dir.local,
ignoreDir ? "" : path.sep + dirName
);
uploadQueue.addTask(
() => put(
path.sep === "/" ? ossFlieName : ossFlieName.replace(/\\/g, "/"),
filePath
)
);
}
}
}
});
});
} catch (error) {
console.log(chalkERROR(`cdn\u811A\u672C\u9519\u8BEF`), error);
}
};
const handleHuaweiObs = function(data) {
const { huaweiObsConfig: cosConfig, huaweiObsFileConfig: cosFileConfig } = data.config;
if (!cosConfig || !cosFileConfig) {
console.log(chalkERROR(`\u534E\u4E3A\u4E91obs\u914D\u7F6E\u9519\u8BEF\uFF01`));
return;
}
const huaweiObsConfig = cosConfig(data);
const huaweiObsFileConfig = cosFileConfig(data);
const { bucket: obsBucket, prefix: obsPrefix } = huaweiObsConfig;
function findFile(inputDir) {
const res = [];
function loop(dirArr) {
for (let i = 0; i < dirArr.length; i += 1) {
const file = dirArr[i];
const filePath = path.resolve(inputDir, file);
const stat = fs.statSync(filePath);
const isDir = stat.isDirectory();
if (!isDir) {
res.push(filePath);
} else {
loop(fs.readdirSync(filePath).map((key) => path.join(file, key)));
}
}
}
let inputDirArr = [];
try {
inputDirArr = fs.readdirSync(inputDir);
} catch (error) {
console.log(chalkERROR(`${inputDir},\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF01`));
throw new Error(`${inputDir},\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF01`);
}
loop(inputDirArr);
return res;
}
try {
const uploadOkRecord = /* @__PURE__ */ new Map();
const uploadErrRecord = /* @__PURE__ */ new Map();
const allFile = [];
const client = new OBS({
access_key_id: huaweiObsConfig.access_key_id,
secret_access_key: huaweiObsConfig.secret_access_key,
// server : 'https://your-endpoint'
server: huaweiObsConfig.server
// 华为obs browser+客户端里面,桶列表,找到对应的桶,看旁边的操作栏里面基本信息,找到Endpoint
});
if (huaweiObsFileConfig.dir) {
allFile.push(...findFile(huaweiObsFileConfig.dir.local));
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6E\u4E0A\u4F20\u672C\u5730\u76EE\u5F55\u5230huawei-obs\u76EE\u5F55"));
}
if (huaweiObsFileConfig.file) {
huaweiObsFileConfig.file.local.forEach((item) => {
allFile.push(item);
});
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6E\u4E0A\u4F20\u672C\u5730\u6587\u4EF6\u5230huawei-obs\u76EE\u5F55"));
}
async function put(obsBucket2, obsFlieName, filePath) {
try {
const result = await new Promise(
(resolve) => {
client.putObject(
{
Bucket: obsBucket2,
Key: obsFlieName,
SourceFile: filePath
},
// eslint-disable-next-line
(err, result2) => {
if (err) {
return resolve({ code: 500, result: result2, err });
}
resolve({ code: 200, result: result2 });
}
);
}
);
const status = result.code;
if (status === 200) {
uploadOkRecord.set(filePath, status);
console.log(
chalkSUCCESS(
`\u4E0A\u4F20huawei-obs\u6210\u529F(${uploadOkRecord.size}/${allFile.length}): ${filePath} ===> ${obsFlieName}`
)
);
} else {
uploadErrRecord.set(filePath, status);
console.log(result);
cache.cos = "error";
console.log(
chalkERROR(
// eslint-disable-next-line
`\u4E0A\u4F20huawei-obs\u5931\u8D25(${uploadErrRecord.size}/${allFile.length}): ${filePath} ===> ${obsFlieName}`
)
);
}
const progress = uploadOkRecord.size + uploadErrRecord.size;
if (progress === allFile.length) {
console.log(
chalkINFO(
`\u6240\u6709\u6587\u4EF6\u4E0A\u4F20huawei-obs\u5B8C\u6210\u3002\u6210\u529F\uFF1A${uploadOkRecord.size}/${allFile.length}\uFF1B\u5931\u8D25\uFF1A${uploadErrRecord.size}/${allFile.length}`
)
);
if (uploadErrRecord.size) {
cache.cos = "error";
console.log(chalkERROR(`\u4E0A\u4F20huawei-obs\u5931\u8D25\u6570\u636E`), uploadErrRecord);
}
}
} catch (error) {
cache.cos = "error";
console.log(chalkERROR(`\u4E0A\u4F20huawei-obs\u9519\u8BEF`), error);
}
}
return new Promise((resolve) => {
const uploadQueue = new ConcurrentPoll({
max: 5,
done: () => resolve("all done~")
});
allFile.forEach((filePath) => {
if (huaweiObsFileConfig.file) {
if (huaweiObsFileConfig.file.local.includes(filePath)) {
const filename = filePath.split(path.sep).pop() || "";
const obsFlieName = path.join(obsPrefix, filename);
uploadQueue.addTask(
() => put(
obsBucket,
path.sep === "/" ? obsFlieName : obsFlieName.replace(/\\/g, "/"),
filePath
)
);
} else {
if (huaweiObsFileConfig.dir) {
const dirName = huaweiObsFileConfig.dir.local.split(path.sep).pop() || "";
const ignoreDir = huaweiObsFileConfig.dir.ignoreDir;
const obsFlieName = obsPrefix + filePath.replace(
huaweiObsFileConfig.dir.local,
ignoreDir ? "" : path.sep + dirName
);
uploadQueue.addTask(
() => put(
obsBucket,
path.sep === "/" ? obsFlieName : obsFlieName.replace(/\\/g, "/"),
filePath
)
);
}
}
}
});
});
} catch (error) {
console.log(chalkERROR(`cdn\u811A\u672C\u9519\u8BEF`), error);
}
};
const handleQiniuKodo = function(data) {
const { qiniuKodoConfig: cosConfig, qiniuKodoFileConfig: cosFileConfig } = data.config;
if (!cosConfig || !cosFileConfig) {
console.log(chalkERROR(`\u4E03\u725B\u4E91kodo\u914D\u7F6E\u9519\u8BEF\uFF01`));
return;
}
const qiniuKodoConfig = cosConfig(data);
const qiniuKodoFileConfig = cosFileConfig(data);
const mac = new qiniu.auth.digest.Mac(
qiniuKodoConfig.accessKey,
qiniuKodoConfig.secretKey
);
const qiniuConfConfig = new qiniu.conf.Config();
qiniuConfConfig.zone = qiniuKodoConfig.zone;
function findFile(inputDir) {
const res = [];
function loop(dirArr) {
for (let i = 0; i < dirArr.length; i += 1) {
const file = dirArr[i];
const filePath = path.resolve(inputDir, file);
const stat = fs.statSync(filePath);
const isDir = stat.isDirectory();
if (!isDir) {
res.push(filePath);
} else {
loop(fs.readdirSync(filePath).map((key) => path.join(file, key)));
}
}
}
let inputDirArr = [];
try {
inputDirArr = fs.readdirSync(inputDir);
} catch (error) {
console.log(chalkERROR(`${inputDir},\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF01`));
throw new Error(`${inputDir},\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF01`);
}
loop(inputDirArr);
return res;
}
try {
const uploadOkRecord = /* @__PURE__ */ new Map();
const uploadErrRecord = /* @__PURE__ */ new Map();
const allFile = [];
if (qiniuKodoFileConfig.dir) {
allFile.push(...findFile(qiniuKodoFileConfig.dir.local));
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6E\u4E0A\u4F20\u672C\u5730\u76EE\u5F55\u5230qiniu\u76EE\u5F55"));
}
if (qiniuKodoFileConfig.file) {
qiniuKodoFileConfig.file.local.forEach((item) => {
allFile.push(item);
});
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6E\u4E0A\u4F20\u672C\u5730\u6587\u4EF6\u5230qiniu\u76EE\u5F55"));
}
async function put(key, filePath) {
try {
const formUploader = new qiniu.form_up.FormUploader(qiniuConfConfig);
const options = {
// eslint-disable-next-line
scope: `${qiniuKodoConfig.bucket}:${key}`
};
const putPolicy = new qiniu.rs.PutPolicy(options);
const uploadToken = putPolicy.uploadToken(mac);
const putExtra = new qiniu.form_up.PutExtra();
const result = await new Promise((resolve) => {
formUploader.putFile(
uploadToken,
key,
filePath,
putExtra,
function(respErr, respBody, respInfo) {
if (respErr) {
return resolve({ code: 500, respErr, respBody, respInfo });
}
if (respInfo.statusCode == 200) {
return resolve({ code: 200 });
} else {
return resolve({ code: 500, respErr, respBody, respInfo });
}
}
);
});
const status = result.code;
if (status === 200) {
uploadOkRecord.set(filePath, status);
console.log(
chalkSUCCESS(
`\u4E0A\u4F20qiniu\u6210\u529F(${uploadOkRecord.size}/${allFile.length}): ${filePath} ===> ${key}`
)
);
} else {
uploadErrRecord.set(filePath, status);
console.log(result);
cache.cos = "error";
console.log(
chalkERROR(
// eslint-disable-next-line
`\u4E0A\u4F20qiniu\u5931\u8D25(${uploadErrRecord.size}/${allFile.length}): ${filePath} ===> ${key}`
)
);
}
const progress = uploadOkRecord.size + uploadErrRecord.size;
if (progress === allFile.length) {
console.log(
chalkINFO(
`\u6240\u6709\u6587\u4EF6\u4E0A\u4F20qiniu\u5B8C\u6210\u3002\u6210\u529F\uFF1A${uploadOkRecord.size}/${allFile.length}\uFF1B\u5931\u8D25\uFF1A${uploadErrRecord.size}/${allFile.length}`
)
);
if (uploadErrRecord.size) {
cache.cos = "error";
console.log(chalkERROR(`\u4E0A\u4F20qiniu\u5931\u8D25\u6570\u636E`), uploadErrRecord);
}
}
} catch (error) {
cache.cos = "error";
console.log(chalkERROR(`\u4E0A\u4F20qiniu\u9519\u8BEF`), error);
}
}
return new Promise((resolve) => {
const uploadQueue = new ConcurrentPoll({
max: 5,
done: () => resolve("all done~")
});
allFile.forEach((filePath) => {
if (qiniuKodoFileConfig.file) {
if (qiniuKodoFileConfig.file.local.includes(filePath)) {
const filename = filePath.split(path.sep).pop() || "";
const key = path.join(qiniuKodoConfig.prefix, filename);
uploadQueue.addTask(
() => put(path.sep === "/" ? key : key.replace(/\\/g, "/"), filePath)
);
} else {
if (qiniuKodoFileConfig.dir) {
const dirName = qiniuKodoFileConfig.dir.local.split(path.sep).pop() || "";
const ignoreDir = qiniuKodoFileConfig.dir.ignoreDir;
const key = qiniuKodoConfig.prefix + filePath.replace(
qiniuKodoFileConfig.dir.local,
ignoreDir ? "" : path.sep + dirName
);
uploadQueue.addTask(
() => put(path.sep === "/" ? key : key.replace(/\\/g, "/"), filePath)
);
}
}
}
});
});
} catch (error) {
console.log(chalkERROR(`cdn\u811A\u672C\u9519\u8BEF`), error);
}
};
const handleTencentCos = function(data) {
const { tencentCosConfig: cosConfig, tencentCosFileConfig: cosFileConfig } = data.config;
if (!cosConfig || !cosFileConfig) {
console.log(chalkERROR(`\u817E\u8BAF\u4E91cos\u914D\u7F6E\u9519\u8BEF\uFF01`));
return;
}
const tencentCosConfig = cosConfig(data);
const tencentCosFileConfig = cosFileConfig(data);
function findFile(inputDir) {
const res = [];
function loop(dirArr) {
for (let i = 0; i < dirArr.length; i += 1) {
const file = dirArr[i];
const filePath = path.resolve(inputDir, file);
const stat = fs.statSync(filePath);
const isDir = stat.isDirectory();
if (!isDir) {
res.push(filePath);
} else {
loop(fs.readdirSync(filePath).map((key) => path.join(file, key)));
}
}
}
let inputDirArr = [];
try {
inputDirArr = fs.readdirSync(inputDir);
} catch (error) {
console.log(chalkERROR(`${inputDir},\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF01`));
throw new Error(`${inputDir},\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF01`);
}
loop(inputDirArr);
return res;
}
try {
const uploadOkRecord = /* @__PURE__ */ new Map();
const uploadErrRecord = /* @__PURE__ */ new Map();
const allFile = [];
const client = new COS({
SecretId: tencentCosConfig.SecretId,
// 推荐使用环境变量获取;用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://www.tencentcloud.com/document/product/598/37140?from_cn_redirect=1
SecretKey: tencentCosConfig.SecretKey,
// 推荐使用环境变量获取;用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://www.tencentcloud.com/document/product/598/37140?from_cn_redirect=1
FileParallelLimit: 10,
// 同一个实例下上传的文件并发数,默认值3
ChunkParallelLimit: 10
// 同一个上传文件的分块并发数,默认值3
});
if (tencentCosFileConfig.dir) {
allFile.push(...findFile(tencentCosFileConfig.dir.local));
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6E\u4E0A\u4F20\u672C\u5730\u6587\u4EF6\u5230tencent-cos\u76EE\u5F55"));
}
if (tencentCosFileConfig.file) {
tencentCosFileConfig.file.local.forEach((item) => {
allFile.push(item);
});
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6E\u4E0A\u4F20\u672C\u5730\u6587\u4EF6\u5230tencent-cos\u76EE\u5F55"));
}
async function put(cosFlieName, filePath) {
try {
const result = await new Promise((resolve) => {
client.putObject(
{
Bucket: tencentCosConfig.Bucket,
Region: tencentCosConfig.Region,
Key: cosFlieName,
StorageClass: tencentCosConfig.StorageClass,
Body: fs.createReadStream(filePath)
// 上传文件对象
// onProgress(progressData) {
// console.log('progressData', JSON.stringify(progressData));
// },
},
function(err, res) {
if (res) {
resolve({ code: 200, res });
} else {
resolve({ code: 400, res, err });
}
}
);
});
const status = result.code;
if (status === 200) {
uploadOkRecord.set(filePath, status);
console.log(
chalkSUCCESS(
`\u4E0A\u4F20tencent-cos\u6210\u529F(${uploadOkRecord.size}/${allFile.length}): ${filePath} ===> ${cosFlieName}`
)
);
} else {
uploadErrRecord.set(filePath, status);
console.log(result);
cache.cos = "error";
console.log(
chalkERROR(
// eslint-disable-next-line
`\u4E0A\u4F20tencent-cos\u5931\u8D25(${uploadErrRecord.size}/${allFile.length}): ${filePath} ===> ${cosFlieName}`
)
);
}
const progress = uploadOkRecord.size + uploadErrRecord.size;
if (progress === allFile.length) {
console.log(
chalkINFO(
`\u6240\u6709\u6587\u4EF6\u4E0A\u4F20tencent-cos\u5B8C\u6210\u3002\u6210\u529F\uFF1A${uploadOkRecord.size}/${allFile.length}\uFF1B\u5931\u8D25\uFF1A${uploadErrRecord.size}/${allFile.length}`
)
);
if (uploadErrRecord.size) {
cache.cos = "error";
console.log(chalkERROR(`\u4E0A\u4F20tencent-cos\u5931\u8D25\u6570\u636E`), uploadErrRecord);
}
}
} catch (error) {
cache.cos = "error";
console.log(chalkERROR(`\u4E0A\u4F20tencent-cos\u9519\u8BEF`), error);
}
}
return new Promise((resolve) => {
const uploadQueue = new ConcurrentPoll({
max: 5,
done: () => resolve("all done~")
});
allFile.forEach((filePath) => {
if (tencentCosFileConfig.file) {
if (tencentCosFileConfig.file.local.includes(filePath)) {
const filename = filePath.split(path.sep).pop() || "";
const cosFlieName = path.join(
tencentCosConfig.prefix || "",
filename
);
uploadQueue.addTask(
() => put(
path.sep === "/" ? cosFlieName : cosFlieName.replace(/\\/g, "/"),
filePath
)
);
} else {
if (tencentCosFileConfig.dir) {
const dirName = tencentCosFileConfig.dir.local.split(path.sep).pop() || "";
const ignoreDir = tencentCosFileConfig.dir.ignoreDir;
const cosFlieName = (tencentCosConfig.prefix || "") + filePath.replace(
tencentCosFileConfig.dir.local,
ignoreDir ? "" : path.sep + dirName
);
uploadQueue.addTask(
() => put(
path.sep === "/" ? cosFlieName : cosFlieName.replace(/\\/g, "/"),
filePath
)
);
}
}
}
});
});
} catch (error) {
console.log(chalkERROR(`cdn\u811A\u672C\u9519\u8BEF`), error);
}
};
var CosEnum = /* @__PURE__ */ ((CosEnum2) => {
CosEnum2["ali"] = "ali";
CosEnum2["huawei"] = "huawei";
CosEnum2["qiniu"] = "qiniu";
CosEnum2["tencent"] = "tencent";
CosEnum2["none"] = "none";
return CosEnum2;
})(CosEnum || {});
var CdnEnum = /* @__PURE__ */ ((CdnEnum2) => {
CdnEnum2["tencent"] = "tencent";
CdnEnum2["none"] = "none";
return CdnEnum2;
})(CdnEnum || {});
function isInstalledGit() {
return new Promise((resolve, reject) => {
exec(
// 'git -v',
"git --version",
{
cwd: process.cwd()
},
(error, stdout, stderr) => {
if (error || stderr) {
console.log(chalkERROR("\u672A\u5B89\u88C5git"));
console.log("error", error);
console.log("stderr", stderr);
reject(error || stderr);
}
if (stdout.length) {
console.log(chalkINFO("\u5DF2\u5B89\u88C5git"), stdout);
resolve("ok");
} else {
console.log(chalkINFO("\u5DF2\u5B89\u88C5git"), stdout);
resolve("ok");
}
}
);
});
}
function gitIsClean() {
return new Promise((resolve, reject) => {
exec(
"git status -s",
{
cwd: process.cwd()
},
(error, stdout, stderr) => {
if (error || stderr) {
reject(error || stderr);
}
if (stdout.length) {
reject(new Error(`\u3010\u63D0\u4EA4commit\u5230\u672C\u5730\u4ED3\u5E93\u3011\u8BF7\u786E\u4FDDgit\u5DE5\u4F5C\u533A\u5E72\u51C0\uFF01`));
} else {
resolve("ok");
}
}
);
});
}
function hasRemoteBranch() {
return new Promise((resolve, reject) => {
const commitBranch = execSync("git branch --show-current", {
cwd: process.cwd()
}).toString().trim();
const remoteBranch = execSync("git ls-remote", {
cwd: process.cwd()
}).toString().trim();
if (remoteBranch.search(`refs/heads/${commitBranch}`) === -1) {
reject(
new Error(
`\u3010\u63D0\u4EA4\u5206\u652F\u5230\u8FDC\u7A0B\u4ED3\u5E93\u3011\u8FDC\u7A0B\u4ED3\u5E93\u4E0D\u5B58\u5728${commitBranch}\u5206\u652F\uFF0C\u8BF7\u5C06${commitBranch}\u5206\u652F\u63D0\u4EA4\u5230\u8FDC\u7A0B\u4ED3\u5E93\uFF01`
)
);
} else {
resolve("ok");
}
});
}
function diffRemote() {
return new Promise((resolve, reject) => {
const commitBranch = execSync("git branch --show-current", {
cwd: process.cwd()
}).toString().trim();
exec(
`git diff --stat ${commitBranch} origin/${commitBranch}`,
{
cwd: process.cwd()
},
(error, stdout, stderr) => {
if (error || stderr) {
console.log(error);
console.log(stderr);
reject(error || stderr);
}
if (stdout.length) {
reject(
new Error(
`\u3010\u63D0\u4EA4commit\u5230\u8FDC\u7A0B\u4ED3\u5E93\u3011\u672C\u5730\u7684${commitBranch}\u5206\u652F\u7684git\u63D0\u4EA4\u548C\u8FDC\u7A0B\u4ED3\u5E93\u7684${commitBranch}\u5206\u652F\u7684git\u63D0\u4EA4\u4E0D\u4E00\u81F4\uFF01`
)
);
} else {
resolve("ok");
}
}
);
});
}
const handleRelease = async (verifyGit, shouldRelease) => {
if (verifyGit) {
await isInstalledGit();
await gitIsClean();
await hasRemoteBranch();
await diffRemote();
} else {
console.log(chalkWARN("\u4E0D\u9A8C\u8BC1git"));
}
if (shouldRelease) {
execSync(`npm run release`, { stdio: "inherit", cwd: process.cwd() });
console.log(chalkSUCCESS("\u66F4\u65B0\u7248\u672C\u5B8C\u6210"));
execSync(`git push --follow-tags`, {
stdio: "inherit",
cwd: process.cwd()
});
console.log(chalkSUCCESS("\u63D0\u4EA4tag\u5B8C\u6210"));
} else {
console.log(chalkWARN("\u4E0D\u6267\u884Crelease"));
}
};
const handleSSH = async function(data) {
const { sshFileConfig, sshConfig } = data.config;
if (!sshConfig || !sshFileConfig)
return;
const serverConfig = sshConfig(data);
const serverFile = sshFileConfig(data);
const ssh = new NodeSSH();
async function connectServer() {
await ssh.connect(serverConfig);
console.log(chalkSUCCESS(`ssh\u8FDE\u63A5${serverConfig.host}\u6210\u529F`));
}
async function uploadFiles() {
if (serverFile.dir) {
const dirName = serverFile.dir.local.split(path.sep).pop() || "";
if (!fs.existsSync(serverFile.dir.local)) {
console.log(chalkERROR(`${serverFile.dir.local},\u4E0D\u5B58\u5728\uFF01`));
return;
}
const localDirectory = serverFile.dir.local;
const oldRemote = serverFile.dir.remote.replace(/\/$/, "");
let oldRemoteDirectory = "";
if (serverFile.dir.ignoreDir) {
oldRemoteDirectory = `${oldRemote}`;
} else {
oldRemoteDirectory = `${oldRemote}/${dirName}`;
}
const remoteDirectory = path.sep === "/" ? oldRemoteDirectory : oldRemoteDirectory.replace(/\\/g, "/");
const dirUploadStatus = await ssh.putDirectory(
localDirectory,
path.sep === "/" ? remoteDirectory : remoteDirectory.replace(/\\/g, "/"),
{
recursive: true,
// 递归
concurrency: 10
// 并发
}
);
console.log(
chalkSUCCESS(
`\u5C06\u672C\u5730\u7684\u76EE\u5F55: ${localDirectory}, \u4E0A\u4F20\u5230${serverConfig.host}\u670D\u52A1\u5668\u76EE\u5F55${remoteDirectory}${dirUploadStatus ? "\u6210\u529F" : "\u5931\u8D25"}`
)
);
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6E\u4E0A\u4F20\u672C\u5730\u76EE\u5F55\u5230\u670D\u52A1\u5668\u76EE\u5F55"));
}
const arr = [];
if (serverFile.file) {
serverFile.file.local.forEach((item) => {
const filename = item.split(path.sep).pop() || "";
if (serverFile.file) {
const oldRemote1 = serverFile.file.remote.replace(/\/$/, "");
const oldRemote2 = `${oldRemote1}/${filename}`;
const newRemote = path.sep === "/" ? oldRemote2 : oldRemote2.replace(/\\/g, "/");
arr.push({
local: item,
remote: newRemote
});
}
});
await ssh.putFiles(arr);
console.log(
chalkSUCCESS(
`\u5C06\u672C\u5730\u7684\u6587\u4EF6: ${serverFile.file.local.join()}, \u4E0A\u4F20\u5230${serverConfig.host}\u670D\u52A1\u5668\u76EE\u5F55${serverFile.file.remote}\u6210\u529F`
)
);
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6E\u4E0A\u4F20\u672C\u5730\u6587\u4EF6\u5230\u670D\u52A1\u5668\u76EE\u5F55"));
}
}
await connectServer();
await uploadFiles();
ssh.dispose();
console.log(chalkSUCCESS(`\u5173\u95ED${serverConfig.host}\u7684ssh`));
};
function calculateRemainingTime({ startTime, endTime }) {
const duration = endTime - startTime;
const ms = 1;
const second = ms * 1e3;
const minute = second * 60;
const hour = minute * 60;
const day = hour * 24;
if (duration > day) {
const res = (duration / day).toFixed(4).split(".");
return `${res[0]}\u5929${Math.floor(Number(`0.${res[1]}`) * 24)}\u65F6`;
} else if (duration > hour) {
const res = (duration / hour).toFixed(4).split(".");
return `${res[0]}\u65F6${Math.floor(Number(`0.${res[1]}`) * 60)}\u5206`;
} else if (duration > minute) {
const res = (duration / minute).toFixed(4).split(".");
return `${res[0]}\u5206${Math.floor(Number(`0.${res[1]}`) * 60)}\u79D2`;
} else {
const res = (duration / second).toFixed(4).split(".");
return `${res[0]}\u79D2`;
}
}
const generateDeployFile = () => {
fs.writeFileSync(
path.resolve(process.cwd(), "deploy.json"),
JSON.stringify(logData(), null, 2)
);
console.log(chalkSUCCESS(`\u751F\u6210\u4E34\u65F6deploy.json\u6587\u4EF6\u5B8C\u6210`));
};
const deleteDeployFile = () => {
fs.rmSync(path.resolve(process.cwd(), "deploy.json"));
console.log(chalkSUCCESS(`\u5220\u9664\u4E34\u65F6deploy.json\u6587\u4EF6\u5B8C\u6210`));
};
const handlePm2Tip = (data) => {
const { sshFileConfig, sshConfig } = data.config;
if (!sshConfig || !sshFileConfig)
return;
const serverConfig = sshConfig(data);
const configFile = [
fs.existsSync(path.resolve(process.cwd(), "nuxt.config.ts")),
fs.existsSync(path.resolve(process.cwd(), "nuxt.config.js")),
fs.existsSync(path.resolve(process.cwd(), "ecosystem.config.js"))
].includes(true);
if (configFile) {
console.log(chalkWARN(`\u68C0\u6D4B\u5230\u5F53\u524D\u9879\u76EE\u9700\u8981\u4F7F\u7528pm2\u7EF4\u62A4\uFF0C\u8BF7\u6267\u884C\u4EE5\u4E0B\u64CD\u4F5C\uFF1A`));
console.log(
chalkWARN(
`1.\u8FDB\u5165${serverConfig.host}\u670D\u52A1\u5668\uFF0C\u518D\u8FDB\u5165\u5BF9\u5E94\u7684\u9879\u76EE\u8DEF\u5F84\uFF0C\u7136\u540E\u91CD\u65B0\u5B89\u88C5\u4F9D\u8D56\uFF08\u5982\u679C\u6CA1\u5F71\u54CDpackage.json\u7684\u53EF\u5FFD\u7565\uFF09`
)
);
console.log(
chalkWARN(
`2.\u6839\u636E\u5F53\u524D\u9879\u76EE\u7684ecosystem.config.js\uFF0C\u91CD\u542F\u5BF9\u5E94\u7684pm2\u670D\u52A1\uFF08\u5982\u679C\u662F\u9996\u6B21\u90E8\u7F72,\u5219\u6267\u884Cpm2 start ecosystem.config.js\uFF09`
)
);
}
};
function checkHasError() {
Object.keys(cache).forEach((key) => {
if (cache[key] === "error") {
console.log(chalkERROR(`${key}\u5931\u8D25`));
process.exit(1);
}
});
}
const deploy = async function(data) {
console.log(chalkSUCCESS(`\u5F00\u59CB\u90E8\u7F72`));
const startTime = new Date().getTime();
const {
shouldBuild = true,
buildCmd,
verifyGit = true,
shouldRelease = true,
config,
deployDoneCb
} = data;
if (!config) {
console.log(chalkERROR("\u7F3A\u5C11config\uFF01"));
return;
}
if (config.cos) {
const allowCos = Object.keys(CosEnum);
if (!allowCos.includes(config.cos(data))) {
console.log(
chalkERROR(
`config.cos\u9519\u8BEF, config.cos\u5FC5\u987B\u662F: ${allowCos.toString()}\u4E4B\u4E00`
)
);
return;
}
}
if (config.cdn) {
const allowCdn = Object.keys(CdnEnum);
if (!allowCdn.includes(config.cdn(data))) {
console.log(
chalkERROR(
`config.cdn\u9519\u8BEF, config.cdn\u5FC5\u987B\u662F: ${allowCdn.toString()}\u4E4B\u4E00`
)
);
return;
}
}
try {
await handleRelease(verifyGit, shouldRelease);
if (shouldBuild) {
console.log(chalkWARN("\u914D\u7F6E\u4E86\u6253\u5305,\u5F00\u59CB\u6267\u884C\u6253\u5305\u547D\u4EE4"));
handleBuild(buildCmd);
checkHasError();
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6E\u6253\u5305"));
}
generateDeployFile();
if (config.cos && config.cos(data)) {
console.log(chalkWARN("\u914D\u7F6E\u4E86cos\u5BF9\u8C61\u5B58\u50A8,\u5F00\u59CB\u6267\u884Ccos\u5BF9\u8C61\u5B58\u50A8\u64CD\u4F5C"));
if (config.cos(data) === CosEnum.none) {
console.log(chalkWARN("\u914D\u7F6E\u4E86cos\u5BF9\u8C61\u5B58\u50A8\u4E3Anone\uFF0C\u4E0D\u6267\u884C"));
} else {
switch (config.cos(data)) {
case CosEnum.huawei:
await handleHuaweiObs(data);
break;
case CosEnum.ali:
await handleAliOss(data);
break;
case CosEnum.qiniu:
await handleQiniuKodo(data);
break;
case CosEnum.tencent:
await handleTencentCos(data);
break;
}
}
checkHasError();
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6E\u5BF9\u8C61\u5B58\u50A8"));
}
if (config.cdn && config.cdn(data)) {
console.log(chalkWARN("\u914D\u7F6E\u4E86cdn\u5185\u5BB9\u5206\u53D1,\u5F00\u59CB\u6267\u884Ccdn\u5185\u5BB9\u5206\u53D1\u64CD\u4F5C"));
if (config.cdn(data) === CdnEnum.none) {
console.log(chalkWARN("\u914D\u7F6E\u4E86cdn\u5185\u5BB9\u5206\u53D1\u4E3Anone\uFF0C\u4E0D\u6267\u884C"));
} else {
switch (config.cdn(data)) {
case CdnEnum.tencent:
await handleTencentCdn(data);
break;
}
}
checkHasError();
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6Ecdn\u5185\u5BB9\u5206\u53D1"));
}
if (config.ssh && config.ssh(data)) {
console.log(chalkWARN("\u914D\u7F6E\u4E86SSH,\u5F00\u59CB\u6267\u884CSSH\u64CD\u4F5C"));
if (config.ssh(data) === false) {
console.log(chalkWARN("\u914D\u7F6E\u4E86SSH\u4E3Afalse\uFF0C\u4E0D\u6267\u884C"));
} else {
await handleSSH(data);
}
checkHasError();
} else {
console.log(chalkWARN("\u6CA1\u6709\u914D\u7F6ESSH"));
}
deleteDeployFile();
const endTime = new Date().getTime();
handlePm2Tip(data);
console.log(
chalkSUCCESS(
`\u90E8\u7F72\u5B8C\u6210\uFF0C\u603B\u8017\u65F6\uFF1A${calculateRemainingTime({
startTime,
endTime
})}`
)
);
deployDoneCb?.({ err: false });
} catch (error) {
console.log(chalkERROR(`\u90E8\u7F72\u51FA\u9519`), error);
deployDoneCb?.({ err: true });
process.exit(1);
}
};
export { CdnEnum, CosEnum, deploy };