@quick-game/cli
Version:
Command line interface for rapid qg development
237 lines (203 loc) • 10.6 kB
JavaScript
;var _WeakMap = require("@babel/runtime-corejs2/core-js/weak-map");var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property");var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor");var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");_Object$defineProperty(exports, "__esModule", { value: true });exports.default = zipPack;var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));var _path = _interopRequireDefault(require("path"));
var _index = require("../../cli-shared-utils/index.js");
var paths = _interopRequireWildcard(require("../lib/paths.js"));
var _manifest = require("../lib/manifest");
var _constanst = require("../lib/constanst.js");
var _readdir = _interopRequireWildcard(require("../lib/readdir.js"));
var _rpk = _interopRequireDefault(require("../lib/rpk.js"));
var _nodeForge = _interopRequireDefault(require("node-forge"));function _getRequireWildcardCache(e) {if ("function" != typeof _WeakMap) return null;var r = new _WeakMap(),t = new _WeakMap();return (_getRequireWildcardCache = function (e) {return e ? t : r;})(e);}function _interopRequireWildcard(e, r) {if (!r && e && e.__esModule) return e;if (null === e || "object" != typeof e && "function" != typeof e) return { default: e };var t = _getRequireWildcardCache(r);if (t && t.has(e)) return t.get(e);var n = { __proto__: null },a = _Object$defineProperty && _Object$getOwnPropertyDescriptor;for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) {var i = a ? _Object$getOwnPropertyDescriptor(e, u) : null;i && (i.get || i.set) ? _Object$defineProperty(n, u, i) : n[u] = e[u];}return n.default = e, t && t.set(e, n), n;}
const crypto = require('crypto');
// 生成签名文件
async function generateReleaseSign(signFile, packageName) {
const cachePath = _path.default.join(__dirname, '../../../node_modules', packageName);
// 判断本地是否有缓存
try {
const privateFile = _path.default.join(cachePath, 'private.pem');
const certificateFile = _path.default.join(cachePath, 'certificate.pem');
if (_index.fs.existsSync(privateFile) && _index.fs.existsSync(certificateFile)) {
(0, _index.info)(`\n####检测到${cachePath}路径下有release签名缓存####`);
// 自动使用缓存
_index.fs.copySync(privateFile, signFile.privatekey);
_index.fs.copySync(certificateFile, signFile.certificate);
return;
}
} catch (err) {
console.error(err);
}
// 使用rl.question()方法从命令行读取证书请求参数
(0, _index.info)('\n本地检测到未包含release签名,正在生成release签名信息...');
const organizationName = packageName;
// 使用包名作为名称
const commonName = packageName;
const countryName = 'CN';
const stateName = 'ZJ';
const localityName = 'HZ';
// 生成RSA密钥对
const keys = _nodeForge.default.pki.rsa.generateKeyPair(2048);
// 创建证书
const cert = _nodeForge.default.pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = '01';
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 10);
const certObj = [
{ name: 'commonName', value: commonName },
{ name: 'countryName', value: countryName },
{ shortName: 'ST', value: stateName },
{ name: 'localityName', value: localityName },
{ name: 'organizationName', value: organizationName },
{ shortName: 'OU', value: 'MINIGAME' }];
cert.setSubject(certObj);
(0, _index.info)('release签名参数为:');
console.log(certObj);
cert.setIssuer(cert.subject.attributes);
cert.setExtensions([
{ name: 'basicConstraints', cA: true },
{ name: 'keyUsage', digitalSignature: true, keyCertSign: true, cRLSign: true }]
);
cert.sign(keys.privateKey);
// 将密钥和证书保存到文件中
const pemPrivateKey = _nodeForge.default.pki.privateKeyToPem(keys.privateKey);
const pemCertificate = _nodeForge.default.pki.certificateToPem(cert);
// 如果release路径不存在
const dirPath = _path.default.dirname(signFile.privatekey);
if (!_index.fs.existsSync(dirPath)) {
_index.fs.mkdirSync(dirPath, { recursive: true });
}
(0, _index.info)(`####签名生成成功####,写入签名privatekey:${signFile.privatekey}`);
_index.fs.writeFileSync(signFile.privatekey, pemPrivateKey);
(0, _index.info)(`写入签名certificate:${signFile.certificate}\n`);
_index.fs.writeFileSync(signFile.certificate, pemCertificate);
// 缓存release签名到本地
try {
if (!_index.fs.existsSync(cachePath)) {
_index.fs.mkdirSync(cachePath, { recursive: true });
_index.fs.writeFileSync(_path.default.join(cachePath, 'private.pem'), pemPrivateKey);
_index.fs.writeFileSync(_path.default.join(cachePath, 'certificate.pem'), pemCertificate);
(0, _index.info)(`####release签名需要保存好!!!#### 该文件已缓存到:${cachePath},若忘记签名,可在此路径下查找。`);
}
} catch (err) {
console.error(err);
}
}
/**
* 判断私钥和证书中的公钥是否一致
* @param {string} privateKeyPath - 私钥 PEM 文件路径
* @param {string} certificatePath - 证书 PEM 文件路径
* @returns {boolean} - 是否匹配
*/
function isKeyPairMatching(privateKeyPath, certificatePath) {
if (!_index.fs.existsSync(privateKeyPath) || !_index.fs.existsSync(certificatePath)) {
console.error('私钥或证书文件不存在');
return false;
}
const privateKeyPem = _index.fs.readFileSync(privateKeyPath, 'utf8');
const certPem = _index.fs.readFileSync(certificatePath, 'utf8');
try {
const privateKey = crypto.createPrivateKey(privateKeyPem);
const publicKeyFromPrivate = crypto.createPublicKey(privateKey).
export({ type: 'spki', format: 'pem' });
const publicKeyFromCert = crypto.createPublicKey(certPem).
export({ type: 'spki', format: 'pem' });
return publicKeyFromPrivate === publicKeyFromCert;
} catch (err) {
console.error('解析密钥或证书失败:', err.message);
return false;
}
}
async function zipPack(isRelease, packageName, singlePackage, subpackages, manifest, buildType, compileStartTime) {
// 签名文件
const signFiles = {
debug: {
privatekey: paths.DEBUG_PRIVATE_KEY,
certificate: paths.DEBUG_CERTIFICATE
},
release: {
privatekey: paths.RELEASE_PRIVATE_KEY,
certificate: paths.RELEASE_CERTIFICATE
}
};
// 编译完成开始打包,打包前先校验签名文件是否存在
const signFile = signFiles[isRelease ? 'release' : 'debug'];
// debug包的后缀 .rpk release包的后缀 .signed.rpk
// buildType 默认值为release
let RPK_SIGNED_NEW = _constanst.RPK_SIGNED;
let RPK_NEW = _constanst.RPK;
if (buildType && !buildType.includes('release')) {
RPK_SIGNED_NEW = `.${buildType}${_constanst.RPK_SIGNED}`;
RPK_NEW = `.${buildType}${_constanst.RPK}`;
}
const ext = isRelease ? RPK_SIGNED_NEW : RPK_NEW;
/**
* 如果本地不存在release签名文件,则自动帮助cp生成release签名文件
*/
if (isRelease) {
if (!_index.fs.existsSync(signFile.privatekey) || !_index.fs.existsSync(signFile.certificate)) {
await generateReleaseSign(signFile, packageName);
}
if (!isKeyPairMatching(signFile.privatekey, signFile.certificate)) {
throw new Error("release签名的certificate.pem与private.pem的公钥不匹配,建议重新生成release签名~");
}
}
if (!_index.fs.existsSync(signFile.privatekey)) {
throw new Error(`${_constanst.LOG_TITLE}缺少私钥文件, 打包失败: ${signFile.privatekey}`);
}
if (!_index.fs.existsSync(signFile.certificate)) {
throw new Error(`${_constanst.LOG_TITLE}缺少证书文件, 打包失败: ${signFile.certificate}`);
}
return new _promise.default((resolve) => {
(0, _index.info)(`${_constanst.LOG_TITLE}${singlePackage ? '原整包编译完成,开始生成原整包' : '分包编译完成,开始生成分包'} 耗时:${new Date().valueOf() - compileStartTime}ms`);
// 遍历出build目录所有文件
const files = (0, _readdir.default)(paths.BUILD, isRelease ? _readdir.noDotSymbolsFiles : _readdir.noDotFiles);
// 如果是打原整包
if (singlePackage) {
// 更新manifest
(0, _manifest.updateLast)();
// 打原整包到temp目录 有插件包的也走这个逻辑,因为需要打个兼容的包
(0, _rpk.default)(`${packageName}${_constanst.RPK}`, files, paths.BUILD, paths.TEMP, signFile, manifest).then(resolve);
} else {
// 如果存在分包,那么就打出各个分包,并和原整包一起再打一个rpk,到dist目录
const pkgRes = [];
// 根据分包配置,找到各个包对应的资源。执行如下操作后,files中剩下的即为主包的资源
subpackages.forEach(({
name,
root
}) => {
const matchFiles = [];
for (let index = files.length - 1; index >= 0; index--) {
const file = files[index];
const ext = _path.default.extname(root);
if (isRelease && !(0, _readdir.noDotSymbolsFiles)(file, 0, _path.default.basename(file))) {
continue;
}
if (ext === '' && file.indexOf(root) === 0) {
// 如果是目录,校验路径
matchFiles.push(files.splice(index, 1)[0]);
} else if (ext !== '' && (file === root || file === `${root}.map`)) {
// 如果是文件,校验是否为 xxx.js 和 xxx.js.map
matchFiles.push(files.splice(index, 1)[0]);
}
}
pkgRes.push({
name,
matchFiles
});
});
// 打各个分包
const rpkPromise = pkgRes.map(({
name,
matchFiles
}) => {
return (0, _rpk.default)(`${name}${_constanst.RPK}`, matchFiles, paths.BUILD, paths.TEMP, signFile);
});
const promiseArr = [(0, _rpk.default)(`${_constanst.MAIN}${_constanst.RPK}`, files, paths.BUILD, paths.TEMP, signFile, manifest)];
// 分包都打好后,连原整包一起打一个整包
_promise.default.all([...rpkPromise, ...promiseArr]).then(function () {
(0, _rpk.default)(`${packageName}${ext}`, (0, _readdir.default)(paths.TEMP), paths.TEMP, paths.DIST, signFile).then(() => {
resolve();
});
});
}
});
}