quickly-build
Version:
mistong web local build script.
482 lines (391 loc) • 14.8 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var fs = require('fs');
var path = require('path');
var simpleGit = require('simple-git');
var rimraf = require('rimraf');
var child_process = require('child_process');
var readline = require('readline');
var chalk = require('chalk');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
var simpleGit__default = /*#__PURE__*/_interopDefaultLegacy(simpleGit);
var rimraf__default = /*#__PURE__*/_interopDefaultLegacy(rimraf);
var readline__default = /*#__PURE__*/_interopDefaultLegacy(readline);
var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
enumerableOnly && (symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
})), keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = null != arguments[i] ? arguments[i] : {};
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
_defineProperty(target, key, source[key]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
return target;
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function () {
var self = this,
args = arguments;
return new Promise(function (resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
const consoleRed = str => console.log(chalk__default['default'].red(str));
const consoleGreen = str => console.log(chalk__default['default'].green(str));
const consoleYellow = str => console.log(chalk__default['default'].blue(str));
const bashCmd = bash => new Promise(res => {
var _job$stdout, _job$stderr;
const job = child_process.exec(bash, {
cwd: process.cwd()
});
console.log(process.cwd());
(_job$stdout = job.stdout) === null || _job$stdout === void 0 ? void 0 : _job$stdout.on('data', data => {
console.info(`stdout: ${data}`);
});
(_job$stderr = job.stderr) === null || _job$stderr === void 0 ? void 0 : _job$stderr.on('data', /*#__PURE__*/function () {
var _ref = _asyncToGenerator(function* (data) {
// console.log(data);
if (/Error/i.test(data)) {
const arr = data.match(/Error:(.|\s|\S)*?(?<=(}|,))/gi);
if (arr) {
arr.forEach(item => consoleRed(item));
}
}
if (/stack/.test(data)) {
const arr = data.match(/stack:(.|\s|\S)*?(?<=(}|,))/g);
if (arr) {
arr.forEach(item => consoleRed(item));
}
} else {
consoleYellow(data);
}
});
return function (_x) {
return _ref.apply(this, arguments);
};
}());
job.on('close', code => {
res(undefined);
});
}); // 交互式选择
const readlineJob = txt => new Promise((reslove, reject) => {
const rl = readline__default['default'].createInterface({
input: process.stdin,
output: process.stdout,
prompt: txt
});
rl.prompt();
rl.on('line', line => {
const res = line.trim();
reslove({
env: res,
rl
}); // rl.close();
}).on('close', () => {
process.exit(0);
});
}); // promise错误重试
const retry = (fnc, time = 1, retryErrorCb) => {
return new Promise((resolve, reject) => {
let index = 0;
const job = () => {
Promise.resolve(fnc(index)).then(res => {
resolve(res);
}).catch(err => {
index++;
retryErrorCb && retryErrorCb(err, index);
index < time ? job() : reject(err);
});
};
job();
});
};
const git = simpleGit__default['default'](); // 检查当前打包分支的文件,再切换回分支
const checkAndBack = /*#__PURE__*/function () {
var _ref = _asyncToGenerator(function* (currentBranch, distBranchName) {
const _yield$git$status = yield git.status(),
files = _yield$git$status.files;
const filePath = [];
for (let i = 0; i < files.length; i++) {
filePath.push(files[i].path);
}
if (filePath.length) {
yield git.add(filePath);
yield git.commit(`build: add files`);
}
yield git.checkout(currentBranch);
if (distBranchName) {
yield git.branch(['-D', distBranchName]);
}
consoleGreen(`✔️ checkout back: '${currentBranch}'` + (distBranchName ? ` and remove dist branch: '${distBranchName}'` : ''));
});
return function checkAndBack(_x, _x2) {
return _ref.apply(this, arguments);
};
}(); // 修改package.json文件
const writePackageJson = () => {
// npm install 时执行的钩子 需要排除
const npmHooksScripts = ['preinstall', 'postinstall', 'prepare', 'install'];
const dataStr = fs__default['default'].readFileSync(path__default['default'].join(__dirname, '../temp/package.hbs'));
const originalDataStr = fs__default['default'].readFileSync(path__default['default'].join(process.cwd(), './package.json'));
const data = JSON.parse(dataStr.toString());
const originalData = JSON.parse(originalDataStr.toString());
const writeData = _objectSpread2(_objectSpread2(_objectSpread2({}, originalData), data), {}, {
scripts: _objectSpread2(_objectSpread2({}, originalData.scripts), data.scripts)
});
delete writeData.husky;
npmHooksScripts.forEach(item => {
if (writeData.scripts[item]) {
delete writeData.scripts[item];
}
});
const writeDataStr = JSON.stringify(writeData, null, 2);
fs__default['default'].writeFileSync(path__default['default'].join(process.cwd(), './package.json'), writeDataStr);
}; // 命令合计
const gitCommitList = /*#__PURE__*/function () {
var _ref2 = _asyncToGenerator(function* (buildEnv, retryTimes, onJobError) {
const _yield$git$branch = yield git.branch({}),
all = _yield$git$branch.all,
currentBranch = _yield$git$branch.current;
buildEnv = buildEnv.trim(); // 检查:不能在含有dist的分支进行打包
if (/_dist_/.test(currentBranch)) {
console.warn(`⚠️ ${currentBranch} build not allow.`);
onJobError(new Error(`${currentBranch} build not allowed`));
return currentBranch;
} // 检查当前版本的名字
const nowName = /^trunk_/.test(currentBranch) && currentBranch.replace('trunk_', '') || /^release_/.test(currentBranch) && currentBranch.replace('release_', '') || currentBranch;
const distName = `${buildEnv === 'sit' || /test/.test(buildEnv) ? 'trunk_dist_' : buildEnv === 'pre' ? 'release_pre_dist_' : 'release_dist_'}${nowName}`;
const find = all.find(item => item === distName); // 删除当前打包分支 重新创建
if (find) {
yield git.branch(['-D', find]);
}
yield git.checkout(['-b', distName]);
consoleGreen(`✔️ switch to ${distName} .`); // 读取package.json模板 写入到文件
writePackageJson(); // 读取.gitignore模板 写入到文件
const igonreData = fs__default['default'].readFileSync(path__default['default'].join(__dirname, '../temp/ignore.hbs'));
fs__default['default'].writeFileSync(path__default['default'].join(process.cwd(), './.gitignore'), igonreData); // 删除package-lock.json
try {
const lockPath = path__default['default'].join(process.cwd(), 'package-lock.json');
if (fs__default['default'].existsSync(lockPath)) {
yield new Promise((res, rej) => {
rimraf__default['default'](lockPath, err => {
if (err) consoleRed(err);
res(undefined);
});
});
}
} catch (error) {} // 进行提交
yield git.add('.');
try {
yield git.commit(`build: ${currentBranch} and auto push`);
} catch (e) {
onJobError(e);
yield checkAndBack(currentBranch, distName);
return distName;
}
try {
// 推送失败尝试3次
yield retry( /*#__PURE__*/function () {
var _ref3 = _asyncToGenerator(function* (retryTime) {
try {
yield git.push('origin', distName, ['-f']); // 强制推送 打包分支只需要最新的dist输出
} catch (e) {
yield new Promise((resolve, reject) => {
setTimeout(resolve, 500);
});
throw new Error('retry');
}
});
return function (_x6) {
return _ref3.apply(this, arguments);
};
}(), retryTimes, (err, num) => {
consoleRed(`⚠️ push fail times: ${num}`);
});
consoleGreen('✔️ commit pushed success.');
} catch (e) {
consoleRed('❌ pushed fail, process exit.');
onJobError(e); // 推送失败 不删除 dist分支
yield checkAndBack(currentBranch); // process.exit(1);
return distName;
} // 切回当前分支
yield checkAndBack(currentBranch, distName);
return distName;
});
return function gitCommitList(_x3, _x4, _x5) {
return _ref2.apply(this, arguments);
};
}(); // 服务器node版本太低 , 本地打包后切换到另一个 xxx_dist_xxx分支,秒发布
const buildJob = /*#__PURE__*/function () {
var _ref4 = _asyncToGenerator(function* ({
buildEnv,
getBuildBashWithEnv = env => `npm run build:${env}`,
pushRetryTimes = 3,
outPutDir = './dist',
onJobError = v => {}
}) {
consoleGreen(`start build env: '${buildEnv}' .`); // 检查是否有未提交的内容
const statusRes = yield git.status();
const files = statusRes.files;
let notCommit = false;
for (let i = 0; i < files.length; i++) {
const path = files[i].path;
consoleRed(`❌ has file not commited, path: ${path}.`);
notCommit = true;
}
if (notCommit) {
onJobError(new Error('❌ has file not commited'));
return '';
} // 编译
const bash = getBuildBashWithEnv(buildEnv);
yield bashCmd(bash);
try {
fs__default['default'].statSync(path__default['default'].join(process.cwd(), outPutDir));
consoleGreen('✔️ 编译完成');
} catch (e) {
onJobError(e);
consoleRed('❌ build fail.');
return '';
}
const distName = yield gitCommitList(buildEnv, pushRetryTimes, onJobError);
try {
yield new Promise((res, rej) => {
rimraf__default['default'](path__default['default'].join(process.cwd(), outPutDir), err => {
if (err) rej(err);
res(undefined);
});
});
} catch (error) {}
consoleGreen(`✔️ ${outPutDir} removed.`);
return distName;
});
return function buildJob(_x7) {
return _ref4.apply(this, arguments);
};
}();
class QuickBuild {
constructor(config) {
var _this = this;
this.quickBuildConfig = void 0;
this.start = /*#__PURE__*/function () {
var _ref = _asyncToGenerator(function* (env) {
let buildEnv;
const argv2 = process.argv[2];
const _this$quickBuildConfi = _this.quickBuildConfig,
onJobSuccess = _this$quickBuildConfi.onJobSuccess,
onJobError = _this$quickBuildConfi.onJobError;
const st = Date.now();
const nextTickDoJob = (v, env) => {
process.nextTick(() => {
const buildTime = Date.now() - st;
const min = Math.floor(buildTime / 60000);
const str = min > 0 ? `${min} min ${((buildTime - min * 60000) / 1000).toFixed(1)}s` : `${(buildTime / 1000).toFixed(1)}s`;
consoleGreen(`✔️ build job use time: ${str}...`);
if (onJobSuccess) {
onJobSuccess(v, env);
}
});
};
if (!env) {
const _this$quickBuildConfi2 = _this.quickBuildConfig.environments,
environments = _this$quickBuildConfi2 === void 0 ? ['sit', 'pre', 'prod'] : _this$quickBuildConfi2;
if (argv2) {
// 从node argv取参数
if (environments.includes(argv2)) {
buildEnv = argv2;
const distName = yield buildJob(_objectSpread2(_objectSpread2({}, _this.quickBuildConfig), {}, {
buildEnv
}));
nextTickDoJob(null, buildEnv);
return distName;
} else {
return consoleRed(`${argv2} not in config.environments`);
}
}
const _yield$readlineJob = yield readlineJob(`enter a build env [ '${environments.join("' | '")}' ] > `),
res = _yield$readlineJob.env,
rlRes = _yield$readlineJob.rl;
buildEnv = res;
const distName = yield buildJob(_objectSpread2(_objectSpread2({}, _this.quickBuildConfig), {}, {
buildEnv,
onJobError
}));
nextTickDoJob(rlRes, buildEnv);
return distName;
} else {
buildEnv = env;
const distName = yield buildJob(_objectSpread2(_objectSpread2({}, _this.quickBuildConfig), {}, {
buildEnv
}));
nextTickDoJob(null, buildEnv);
return distName;
}
});
return function (_x) {
return _ref.apply(this, arguments);
};
}();
const _environments = config.environments;
if (_environments) {
const check = _environments.filter(item => typeof item !== "string");
if (check.length) {
throw new Error(`config.environments must be Array<string> !`);
}
}
this.quickBuildConfig = config;
}
}
var main = {
QuickBuild,
git
};
exports.QuickBuild = QuickBuild;
exports.default = main;