@honor-minigame/cli
Version:
honor minigame pack cli
197 lines (176 loc) • 7.3 kB
JavaScript
import path from 'path'
import fs from 'fs-extra'
import { fileURLToPath } from 'url'
import { info } from '../../utils/logger.js'
import * as paths from '../lib/paths.js'
import { MAIN, RPK, RPK_SIGNED, LOG_TITLE } from '../lib/constanst.js'
import readdir, { noDotSymbolsFiles, noDotFiles } from '../lib/readdir.js'
import rpk from '../lib/rpk.js'
import forge from 'node-forge'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
// 生成签名文件
async function generateReleaseSign(signFile, packageName) {
const cachePath = path.join(__dirname, '../../../node_modules', packageName)
// 判断本地是否有缓存
try {
const privateFile = path.join(cachePath, 'private.pem')
const certificateFile = path.join(cachePath, 'certificate.pem')
if (fs.existsSync(privateFile) && fs.existsSync(certificateFile)) {
info(`\n####检测到${cachePath}路径下有release签名缓存####`)
// 自动使用缓存
fs.copySync(privateFile, signFile.privatekey)
fs.copySync(certificateFile, signFile.certificate)
return
}
} catch (err) {
console.error(err)
}
// 使用rl.question()方法从命令行读取证书请求参数
info('\n本地检测到未包含release签名,正在生成release签名信息...')
const organizationName = packageName
// 使用包名作为名称
const commonName = packageName
const countryName = 'CN'
const stateName = 'ZJ'
const localityName = 'HZ'
// 生成RSA密钥对
const keys = forge.pki.rsa.generateKeyPair(2048)
// 创建证书
const cert = forge.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)
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 = forge.pki.privateKeyToPem(keys.privateKey)
const pemCertificate = forge.pki.certificateToPem(cert)
// 如果release路径不存在
const dirPath = path.dirname(signFile.privatekey)
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true })
}
info(`####签名生成成功####,写入签名privatekey:${signFile.privatekey}`)
fs.outputFileSync(signFile.privatekey, pemPrivateKey)
info(`写入签名certificate:${signFile.certificate}\n`)
fs.outputFileSync(signFile.certificate, pemCertificate)
// 缓存release签名到本地
try {
if (!fs.existsSync(cachePath)) {
fs.mkdirSync(cachePath, { recursive: true })
fs.outputFileSync(path.join(cachePath, 'private.pem'), pemPrivateKey)
fs.outputFileSync(path.join(cachePath, 'certificate.pem'), pemCertificate)
info(`####release签名需要保存好!!!#### 该文件已缓存到:${cachePath},若忘记签名,可在此路径下查找。`)
}
} catch (err) {
console.error(err)
}
}
export default 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 = RPK_SIGNED
let RPK_NEW = RPK
if (buildType && !buildType.includes('release')) {
RPK_SIGNED_NEW = `.${buildType}${RPK_SIGNED}`
RPK_NEW = `.${buildType}${RPK}`
}
const ext = isRelease ? RPK_SIGNED_NEW : RPK_NEW
/**
* 如果本地不存在release签名文件,则自动帮助cp生成release签名文件
*/
if (isRelease) {
if (!fs.existsSync(signFile.privatekey) || !fs.existsSync(signFile.certificate)) {
await generateReleaseSign(signFile, packageName)
}
}
if (!fs.existsSync(signFile.privatekey)) {
throw (new Error(`${LOG_TITLE}缺少私钥文件, 打包失败: ${signFile.privatekey}`))
}
if (!fs.existsSync(signFile.certificate)) {
throw (new Error(`${LOG_TITLE}缺少证书文件, 打包失败: ${signFile.certificate}`))
}
return new Promise(resolve => {
info(`${LOG_TITLE}${singlePackage ? '原整包编译完成,开始生成原整包' : '分包编译完成,开始生成分包'} 耗时:${(new Date()).valueOf() - compileStartTime}ms`)
// 遍历出build目录所有文件
const files = readdir(paths.BUILD, isRelease ? noDotSymbolsFiles : noDotFiles)
// 如果是打原整包
if (singlePackage) {
// 打原整包到temp目录 有插件包的也走这个逻辑,因为需要打个兼容的包
rpk(`${packageName}${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.extname(root)
if (isRelease && !noDotSymbolsFiles(file, 0, path.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 rpk(`${name}${RPK}`, matchFiles, paths.BUILD, paths.TEMP, signFile)
})
const promiseArr = [rpk(`${MAIN}${RPK}`, files, paths.BUILD, paths.TEMP, signFile, manifest)]
// 分包都打好后,连原整包一起打一个整包
Promise.all([...rpkPromise, ...promiseArr]).then(function () {
rpk(`${packageName}${ext}`, readdir(paths.TEMP), paths.TEMP, paths.DIST, signFile).then(() => {
resolve()
})
})
}
})
}