@mpsc/cli
Version:
266 lines (233 loc) • 8.39 kB
JavaScript
const _log = require('../utils/logger');
const ci = require('miniprogram-ci');
const path = require('path');
const { getMpsAppJson } = require('./getProjectJson');
const { isArray } = require('../utils/type');
const { timestampToTime } = require('../utils/common');
const execa = require('execa');
const { getCommit, getUser, getBranch, getRemote } = require('./git');
const isDebug = globalThis['buildDebug'] || false;
const { callHook } = require('./hook');
const { getFilesMapWithExtension } = require('../utils/file');
const { logWithSpinner, successSpinner } = require('../utils/spinner');
function getProject(appConfig) {
return new ci.Project({
appid: appConfig.appId,
type: 'miniProgram',
projectPath: appConfig.projectPath,
privateKeyPath: appConfig.privateKeyPath,
ignores: ['node_modules/**/*', '.mps/**/*'],
});
}
const uploadMp = async (prompt, mpConfig, buildSuccessAppNames, isFromServer) => {
const project = getProject(mpConfig);
isDebug && _log.info(JSON.stringify(project), 'getProject');
logWithSpinner(_log.chalk.green('⚓'), `${mpConfig.appName} 小程序构建中...`);
try {
const uploadResult = await ci.upload({
project,
version: prompt.version,
desc: prompt.desc,
setting: {
es6: true,
es7: true,
},
onProgressUpdate: (e) => _log.emitLog('info', 'ci.upload', JSON.stringify(e)),
// robot: prompt.isProd ? 1 : 2,
});
buildSuccessAppNames.push(mpConfig.appName);
isDebug && _log.info(JSON.stringify(uploadResult), 'uploadResult');
_log.info(`${mpConfig.appName} 上传成功,请自行到微信后台设置体验版`, 'uploadMp');
} catch (error) {
_log.error(`${mpConfig.appName} 上传微信后台失败,原因:${error}`, 'uploadMp');
!isFromServer && process.exit(1);
} finally {
successSpinner();
}
};
const buildPreview = async (prompt, mpConfig, isFromServer) => {
const project = getProject(mpConfig);
isDebug && _log.info(JSON.stringify(project), 'getProject');
logWithSpinner(_log.chalk.green('⚓'), `${mpConfig.appName} 小程序构建中...`);
try {
const qrcodeName = `${mpConfig.appName}-有效期至${timestampToTime(
+new Date() + 25 * 60 * 1000,
)}`;
const qrcodeOutputDest = path.join(process.cwd(), `.mps/previewQrCode/${qrcodeName}.jpg`);
const previewResult = await ci.preview({
project,
desc: prompt.desc,
setting: {
es6: true,
es7: true,
minifyJS: true,
minifyWXML: true,
minifyWXSS: true,
minify: true,
autoPrefixWXSS: true,
},
pagePath: mpConfig.prePagePath,
qrcodeFormat: 'image',
qrcodeOutputDest,
onProgressUpdate: (e) => _log.emitLog('info', 'ci.preview', JSON.stringify(e)),
});
isDebug && _log.info(JSON.stringify(previewResult), 'buildPreview');
_log.info(`${mpConfig.appName} 设置预览成功`, 'uploadMp');
} catch (error) {
_log.error(`${mpConfig.appName} 生成预览版失败,原因:${error}`, 'buildPreview');
!isFromServer && process.exit(1);
} finally {
successSpinner();
}
};
/**
* 2024-11-18 16:19:57
* @author Mint.Yan
* @description miniProgram build
* @param {boolean} isFromServer 是否来自web服务构建
*/
module.exports = async (answer, isFromServer = false) => {
const isLog = globalThis['buildLog'] || false;
if (isLog) {
const capturedFileName = timestampToTime(+new Date()) + '_mps.log';
await execa('touch', [capturedFileName]);
_log.writeLog(capturedFileName);
}
_log.info('即将开始构建小程序', 'buildMp');
const mpsJson = getMpsAppJson();
// 项目路径,即 project.config.json 所在的目录
const projectPath = path.join(process.cwd(), mpsJson.projectPath || '');
const weapps = mpsJson.weapps;
if (!isArray(weapps) || !weapps.length) {
_log.error(
`没有发现任何小程序配置,请检查 ${_log.chalk.yellow(
'.mps/apps.json',
)} 文件中 ${_log.chalk.yellow('weapps')} 字段是否配置正确`,
'buildMp',
);
}
// 执行build构建
const manager = mpsJson.manager;
const command = mpsJson.command;
// !mpsJson.projectPath 代表为微信小程序原生,构建是通过开发者工具完成的
if (mpsJson.projectPath) {
const args = manager === 'yarn' ? [command] : ['run', command];
logWithSpinner(
'⚓',
`小程序执行${manager} ${manager === 'yarn' ? command : 'run ' + command} 中...`,
);
execa.sync(manager, args, {
cwd: process.cwd(),
});
successSpinner('执行完成');
}
// 本地版 先清空qrcode目录
if (!answer.isProd) {
execa('mpsc', ['clean'], {
cwd: process.cwd(),
});
}
await callHook('beforeBuild');
const buildSuccessAppNames = [];
for (let index = 0; index < weapps.length; index++) {
const weapp = weapps[index];
const appId = weapp.appId;
const appName = weapp.appName;
// 预览路径
const prePagePath = weapp.prePagePath || '';
// 密钥路径
const privateKeyPath = path.join(process.cwd(), '.mps/secrets/private.' + appId + '.key');
await callHook('beforeTaskBuild');
const mpInfo = {
appId,
appName,
prePagePath,
projectPath,
privateKeyPath,
};
if (answer.isProd) {
await uploadMp(answer, mpInfo, buildSuccessAppNames, isFromServer);
} else {
await buildPreview(answer, mpInfo, isFromServer);
}
await callHook('afterTaskBuild');
}
await callHook('afterBuild');
// 生成tag
if (answer.isCreateTag) {
const tagName = await getCommit();
try {
execa.sync('git', ['tag', tagName], {
cwd: process.cwd(),
});
} catch (error) {
_log.error('生成tag失败:' + error, 'TagName');
}
try {
execa.sync('git', ['push', 'origin', tagName], {
cwd: process.cwd(),
});
} catch (error) {
_log.error('推送tag失败:' + error, 'TagName');
}
_log.info(`TAG: ${tagName} 推送成功`, 'TagName');
}
_log.done('小程序构建完成', 'build');
console.log('');
// 群通知 | web服务
if (answer.groupNotice || isFromServer) {
answer.groupNotice && _log.done('群通知任务执行中...', 'build');
const extraInfo = {
user: await getUser(),
branch: await getBranch(),
reomte: await getRemote(),
tag: answer.isCreateTag ? await getCommit() : '',
atUsers: mpsJson.atUsers || [],
// prod 提供构建成功小程序的名称
// dev 版本不需要,直接从qrcode filename 获取
buildSuccessAppNames: answer.isProd ? buildSuccessAppNames.join(',') : '',
};
const options = {
isProd: answer.isProd,
extraInfo,
};
const { sendMessage } = require('../lib/http');
if (answer.isProd) {
options.qrcodeFiles = [
{
baseUrl:
'https://www.yanquankun.cn/cdn/mpsc/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%8A%A9%E6%89%8B.jpeg',
fileName: '微信扫码右侧小程序助手二维码访问最新体验版,如需设置体验版请联系上述开发人员',
},
];
isFromServer &&
sendMessage({
level: 'done',
message: '推送小程序后台成功,如需设置体验版请联系上述开发人员',
});
} else {
const qrcodePath = path.join(process.cwd(), '.mps/previewQrCode/');
const qrcodeFiles = await getFilesMapWithExtension(qrcodePath, '.jpg');
options.qrcodeFiles = qrcodeFiles;
if (isFromServer) {
const { qrcodeMap, serverInfo } = require('../lib/http');
const { guid } = require('../utils/common');
const taskId = guid();
const url = `http://${serverInfo.hostIP}:${serverInfo.port}/${process.env.projectName}?id=${taskId}`;
options.isWebUrl = url;
// cache build result
qrcodeMap.set(taskId, { ...options, qrcodeFiles });
sendMessage({
url,
buildStatus: 'done',
});
}
}
if (answer.groupNotice) {
await callHook('noticeTask', options);
}
}
answer.isAtuoUpdateQrcode &&
_log.warn('已开启自动更新本地版二维码任务,请勿关闭当前命令窗口', 'Warn!!!');
!answer.isAtuoUpdateQrcode && !isFromServer && process.exit(1);
};