UNPKG

@cloudbase/lowcode-deployer

Version:

deploy weda app

843 lines (842 loc) 40.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.deployer = exports.EDeployMode = exports.TCBAPICloudApiService = exports.LocalStorage = void 0; const fs_extra_1 = __importDefault(require("fs-extra")); const archiver_1 = __importDefault(require("archiver")); const miniprogram_ci_1 = require("miniprogram-ci"); const qrcode_1 = __importDefault(require("qrcode")); const path_1 = __importDefault(require("path")); const cloud_api_1 = require("@cloudbase/cloud-api"); const cloud_api_service_1 = require("./utils/tce-cloud-api/cloud_api_service"); const manager_node_1 = __importDefault(require("@cloudbase/manager-node")); const parallel_1 = require("@cloudbase/manager-node/lib/utils/parallel"); const private_1 = require("./config/private"); const static_builder_1 = require("@cloudbase/static-builder"); const emoji_1 = require("./utils/emoji"); const lowcode_builder_1 = require("@cloudbase/lowcode-builder"); const storage_1 = require("./utils/storage"); const url_1 = __importDefault(require("url")); const tcb_api_capi_1 = require("./utils/tcb-api-capi"); var tcb_api_capi_2 = require("./utils/tcb-api-capi"); Object.defineProperty(exports, "LocalStorage", { enumerable: true, get: function () { return tcb_api_capi_2.LocalStorage; } }); Object.defineProperty(exports, "TCBAPICloudApiService", { enumerable: true, get: function () { return tcb_api_capi_2.TCBAPICloudApiService; } }); const NOT_NPM_ERROR = '__NO_NODE_MODULES__ NPM packages not found'; const QRCODE_PATH = './qrcode.jpg'; const RUNTIME_PROTOCOL = process.env.RUNTIME_PROTOCOL || 'https'; class Deployer { constructor(options) { // this.app = options.cloudbaseManager; // this.logger = getLogger(); this.logger = options.logger || console; this.api = options.api || { emoji: emoji_1.emoji, projectPath: process.cwd() }; } } var EDeployMode; (function (EDeployMode) { EDeployMode["PRODUCTION"] = "PRODUCTION"; EDeployMode["PREVIEW"] = "PREVIEW"; })(EDeployMode = exports.EDeployMode || (exports.EDeployMode = {})); class LowcodeDeployer extends Deployer { constructor(options) { super(options); this.credential = options.credential; this.account = options.account; this.resolvedInputs = this.normalizInputs(options); const { type, mpAppId, projectPath, ignores, miniprogramOptions } = this.resolvedInputs; this._localstorage = new tcb_api_capi_1.LocalStorage(this.api.projectPath); this.lowcodeService = this._generateLowcodeService(this.credential); if (type === 'MP') { this._projectOptions = { mpAppId, projectPath, privateKeyPath: miniprogramOptions === null || miniprogramOptions === void 0 ? void 0 : miniprogramOptions.privateKeyPath, ignores: Array.from(new Set(((miniprogramOptions === null || miniprogramOptions === void 0 ? void 0 : miniprogramOptions.privateKeyPath) ? [miniprogramOptions === null || miniprogramOptions === void 0 ? void 0 : miniprogramOptions.privateKeyPath] : []).concat([ 'node_modules/**/*', 'cloudbaserc.json', '.env', ...ignores, ]))), ...miniprogramOptions === null || miniprogramOptions === void 0 ? void 0 : miniprogramOptions.projectConfig, }; } else { this.tcbService = this._generateTcbService(this.credential); this.cloudbaseManager = new manager_node_1.default({ ...this.credential, envId: this.resolvedInputs.envId, }); } } /** * miniprogram-ci 疑似有问题 * 每次 project 创建出来其缓存的文件map就已确定并且无法更新 * 因此每次使用除了受控的情况,都临时新建 project 对象 */ get mpCIProject() { const { miniprogramOptions } = this.resolvedInputs; return (miniprogramOptions === null || miniprogramOptions === void 0 ? void 0 : miniprogramOptions.ciProject) || this._generateMpCIProject(this._projectOptions); } normalizInputs(options) { var _a, _b, _c; const resolvedInputs = { ...options, outputPath: options.outputPath || options.projectPath, ignores: options.ignores || [], }; if (resolvedInputs.type === 'MP') { const { miniprogramOptions = {}, projectPath } = resolvedInputs; let { privateKey, privateKeyPath = '' } = miniprogramOptions; if (privateKey) { const keyPath = path_1.default.resolve(projectPath, './private.key'); const key = Buffer.from(privateKey, 'base64'); fs_extra_1.default.writeFileSync(keyPath, key.toString()); privateKeyPath = keyPath; } if (!privateKeyPath || !fs_extra_1.default.existsSync(path_1.default.resolve(projectPath, privateKeyPath))) { throw new Error('need privateKey or privateKey in miniprogramOptions config'); } miniprogramOptions.privateKeyPath = privateKeyPath; resolvedInputs.miniprogramOptions = miniprogramOptions; } else if (resolvedInputs.type === 'WEB') { const requiredValues = ['appId', 'envId', 'credential']; for (let key of requiredValues) { if (!resolvedInputs[key]) { throw new Error(`web 部署模式缺乏必须参数 ${key}`); } } let { webOptions = {} } = resolvedInputs; if (this.isPrivate && !webOptions.credential) { throw new Error('web 私有化部署缺乏必须参数 webOptions.credential'); } if (!webOptions.cloudPath) { webOptions.cloudPath = this._getDefaultWebRootPath(resolvedInputs); } if (!webOptions.buildTypeList) { webOptions.buildTypeList = []; } resolvedInputs.webOptions = webOptions; } if (resolvedInputs.artifactOptions) { if (!resolvedInputs.artifactOptions.baseKey) { throw new Error('缺少制品持久化目标地址配置'); } if (!resolvedInputs.artifactOptions.bucket) { throw new Error('缺少制品持久存储桶配置'); } resolvedInputs.artifactOptions = { ...resolvedInputs.artifactOptions, credential: resolvedInputs.artifactOptions.credential || (this.isPrivate ? (_a = resolvedInputs.webOptions) === null || _a === void 0 ? void 0 : _a.credential : this.credential), endpoint: resolvedInputs.artifactOptions.endpoint || ((_b = resolvedInputs.webOptions) === null || _b === void 0 ? void 0 : _b.endpoint), region: resolvedInputs.artifactOptions.region || ((_c = resolvedInputs.webOptions) === null || _c === void 0 ? void 0 : _c.region), }; } return resolvedInputs; } get isPrivate() { return process.env.IS_PRIVATE === 'true'; } _getDefaultWebRootPath(inputs) { let { appId, mode } = inputs; return mode === EDeployMode.PREVIEW ? `/${appId}/preview/` : `/${appId}/production/`; } async deploy(options = {}) { var _a, _b, _c, _d, _e, _f, _g; const { type, mode, appId, envId } = this.resolvedInputs; let res; let qrcodeOutputPath = ''; try { if (type === 'MP') { try { await this.genearteMiniprogramNPM(); const list = await this._cleanNonRefFile(); (_b = (_a = this.logger) === null || _a === void 0 ? void 0 : _a.info) === null || _b === void 0 ? void 0 : _b.call(_a, `清理未使用文件${list.join('\n')}`); } catch (e) { (_d = (_c = this.logger) === null || _c === void 0 ? void 0 : _c.debug) === null || _d === void 0 ? void 0 : _d.call(_c, e.message); } res = await (mode === EDeployMode.PRODUCTION ? this._ciUpload(options) : this._ciPreview(options)); qrcodeOutputPath = res.link; } else if (type === 'WEB') { const { webOptions } = this.resolvedInputs; try { await this.lowcodeService.request('UpdateLoginConfig', { EnvId: this.resolvedInputs.envId }); (_e = this.logger) === null || _e === void 0 ? void 0 : _e.debug('login config set success'); } catch (e) { this.logger.error(`设置登录配置失败 ${e.message}}`); } const { website, domains = [] } = await this._preProcessWebDeploy(options === null || options === void 0 ? void 0 : options.version); const currentFielMap = {}; if (!(0, lowcode_builder_1.buildAsXPageByBuildType)(webOptions.buildTypeList)) { const ignores = Array.from(new Set(['.tmp', '.git', '.github', 'node_modules/**', 'cloudbaserc.js', ...this.resolvedInputs.ignores])); const includes = ['**', ...ignores.map((ignore) => `!${ignore}`)]; const builder = new static_builder_1.StaticBuilder({ projectPath: path_1.default.resolve(this.resolvedInputs.projectPath), }); const { static: staticList = [], staticConfig = [] } = await builder.build(includes, { path: this.resolvedInputs.webOptions.cloudPath, domain: this.isPrivate ? undefined : website === null || website === void 0 ? void 0 : website.CdnDomain, }); const deployContent = [...staticList]; if (this.isPrivate) { const { endpoint, region, bucket, credential } = webOptions; const instance = new storage_1.Storage({ endpoint, credential, region, cos: false }); const fileList = deployContent.reduce((list, meta) => { let { cloudPath } = meta; if (cloudPath.startsWith('/')) { cloudPath = cloudPath.substring(1); } list.push(...(0, storage_1.getUploadDirFileList)(meta.src, cloudPath)); return list; }, []); fileList.forEach((meta) => { currentFielMap[meta.cloudPath] = true; }); const asyncTaskController = new parallel_1.AsyncTaskParallelController(20, 50); asyncTaskController.loadTasks(fileList.map((meta) => { return async () => { try { const param = { Bucket: bucket, ACL: 'public-read', Body: fs_extra_1.default.createReadStream(meta.src), Key: meta.cloudPath, }; try { await instance.putObject(param); } catch (e) { // 间隔重试一次 await new Promise((resolve, reject) => { setTimeout(() => { resolve(null); }, 500); }); await instance.putObject(param); } return { code: 0 }; } catch (e) { if (!e.code) { e.code = -1; } throw e; } }; })); (_f = this.logger) === null || _f === void 0 ? void 0 : _f.debug('开始部署文件...'); const list = await asyncTaskController.run(); if (list.some((item) => item.code)) { console.log(list.filter((item) => item.code)); throw new Error('部分文件部署失败'); } } else { deployContent.push(...staticConfig); /** * 私有化场景该文件交给初始化生成,不再兜底 * 只有公有场景下防止环境未初始化情形,进行一次兜底 * 检测当线上文件不存在时进行兜底写入 */ let privateJsPath; try { const { Contents = [] } = await this.cloudbaseManager.hosting.findFiles({ prefix: 'weda-config', }); if (!((_g = Contents === null || Contents === void 0 ? void 0 : Contents.find) === null || _g === void 0 ? void 0 : _g.call(Contents, (item) => item.Key === `weda-config/${private_1.PRIVATE_JS_NAME}`))) { privateJsPath = path_1.default.join(this.resolvedInputs.projectPath, '.tmp', private_1.PRIVATE_JS_NAME); await fs_extra_1.default.ensureFile(privateJsPath); await fs_extra_1.default.writeFile(privateJsPath, private_1.PRIVATE_JS_CODE, { encoding: 'utf8', flag: 'w', }); deployContent.push({ src: privateJsPath, cloudPath: `/weda-config/${private_1.PRIVATE_JS_NAME}` }); } } catch (e) { this.logger.error('创建私有化JS文件失败:', e); throw e; } deployContent.reduce((map, meta) => { let { cloudPath } = meta; if (cloudPath.startsWith('/')) { cloudPath = cloudPath.substring(1); } const fileList = (0, storage_1.getUploadDirFileList)(meta.src, cloudPath); fileList.forEach((item) => { map[item.cloudPath] = true; }); return map; }, currentFielMap); await Promise.all(deployContent.map((options) => this.cloudbaseManager.hosting.uploadFiles({ localPath: options.src, cloudPath: options.cloudPath, ignore: ignores, }))); // 暂不启用清理逻辑 this._postCleanWebCloudStorage(currentFielMap); try { if (privateJsPath) { await fs_extra_1.default.remove(privateJsPath); } } catch (e) { } } try { await builder.clean(); } catch (e) { } } else { this.logger.info('xpage 跳过部署流程'); } res = await this._postProcessWebDeploy({ domains }); qrcodeOutputPath = res.qrCode; } try { let publishAppCustomNavRes = await this.lowcodeService.request('PublishAppCustomNav', { WeAppId: appId, EnvId: envId, PublishType: mode === EDeployMode.PRODUCTION ? 'product' : 'preview', }); this.logger.info(`${this.api.emoji('🚀')} custom publish success: ${JSON.stringify(publishAppCustomNavRes)}`); } catch (e) { this.logger.error(`${this.api.emoji('🚀')} custom publish fail: ${JSON.stringify(e)}`); if ((e === null || e === void 0 ? void 0 : e.code) !== 'ResourceNotFound' && !/app does not exist/.test(e === null || e === void 0 ? void 0 : e.message) && !/应用不存在/.test(e === null || e === void 0 ? void 0 : e.message)) { throw e; } } return res; } catch (e) { throw e; } finally { await this._persistArtifactOptions(qrcodeOutputPath); } } async _postCleanWebCloudStorage(currentFielMap = {}) { var _a, _b; const { webOptions } = this.resolvedInputs; try { if (!currentFielMap || !Object.keys(currentFielMap).length) { return; } } catch (e) { return; } try { let prefix = webOptions.cloudPath; if (prefix) { if (prefix === null || prefix === void 0 ? void 0 : prefix.startsWith('/')) { prefix = prefix.substring(1); } const { Contents = [] } = await this.cloudbaseManager.hosting.findFiles({ prefix, }); const reg = new RegExp(`^${prefix}`); const deleteFiles = (_a = Contents === null || Contents === void 0 ? void 0 : Contents.map) === null || _a === void 0 ? void 0 : _a.call(Contents, (item) => item.Key).filter((key) => { return reg.test(key) && /\/index.html$/.test(key) && !currentFielMap[key]; }); if (deleteFiles.length) { const hosting = await this.cloudbaseManager.hosting.checkStatus(); const { Bucket, Regoin } = hosting; const storageService = await this.cloudbaseManager.hosting.environment.getStorageService(); const storage = new storage_1.Storage({ region: Regoin, cos: !this.isPrivate, instance: storageService.getCos(), }); const sliceGroup = []; const delta = 500; const total = Math.ceil(deleteFiles.length / delta); for (let i = 0; i < total; i++) { sliceGroup.push(deleteFiles.slice(i * delta, (i + 1) * delta)); } const asyncTaskController = new parallel_1.AsyncTaskParallelController(5, 50); asyncTaskController.loadTasks(sliceGroup.map((list) => { return async () => { try { await storage.deleteMultipleObject({ Bucket, Objects: list.map((cloudPath) => { return { Key: cloudPath }; }), }); return { code: 0 }; } catch (e) { if (!e.code) { e.code = -1; } throw e; } }; })); (_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug('开始清理云端文件...'); await asyncTaskController.run(); this.logger.info(`${this.api.emoji('🚀')} 清理云端成功:`, deleteFiles); } } } catch (e) { } } async _persistArtifactOptions(qrcodeOutputPath = '') { if (this.resolvedInputs.artifactOptions) { const { endpoint, bucket, region, baseKey, credential } = this.resolvedInputs.artifactOptions; try { const tmpPath = path_1.default.resolve('.tmp'); fs_extra_1.default.ensureDirSync(tmpPath); fs_extra_1.default.ensureDirSync(this.resolvedInputs.projectPath); const zipPath = path_1.default.resolve('.tmp', `${this.resolvedInputs.appId}.zip`); await zipDir(path_1.default.resolve(this.resolvedInputs.projectPath), zipPath); const storage = new storage_1.Storage({ endpoint, region, credential: credential, cos: !this.isPrivate, }); await storage.putObject({ ACL: 'public-read', Bucket: bucket || '', Key: path_1.default.posix.join(baseKey, 'dist.zip'), Body: fs_extra_1.default.createReadStream(zipPath), }); fs_extra_1.default.removeSync(zipPath); if (!qrcodeOutputPath) { qrcodeOutputPath = path_1.default.resolve(this.resolvedInputs.outputPath, QRCODE_PATH); } if (fs_extra_1.default.existsSync(qrcodeOutputPath)) { await storage.putObject({ ACL: 'public-read', Bucket: bucket || '', Key: path_1.default.posix.join(baseKey, 'qrcode.jpg'), Body: fs_extra_1.default.createReadStream(qrcodeOutputPath), }); } this.logger.info(`${this.api.emoji('🚀')} 持久化制品成功。`); } catch (e) { this.logger.error(`${this.api.emoji('🚀')} 持久化上传制品失败:`, e); } } } async _preProcessWebDeploy(version = '') { var _a, _b; const { envId, webOptions = {}, projectPath } = this.resolvedInputs; let domains, currentWebsite; if (this.isPrivate) { const { domain } = webOptions; domains = [domain]; currentWebsite = { CdnDomain: domain }; } else { try { let [website, hostingDatas] = await this._getHostingInfoTask(envId, false); if (!website) { throw new Error('静态托管初始化超时'); } domains = hostingDatas.map((item) => item.CdnDomain); currentWebsite = website; } catch (e) { this.logger.error('获取静态托管失败: ', e); throw e; } } const MAINIFEST_PATH = path_1.default.join(projectPath, 'weda-manifest.json'); if (fs_extra_1.default.existsSync(MAINIFEST_PATH)) { const manifestJson = fs_extra_1.default.readJsonSync(MAINIFEST_PATH); let { preHeatUrls, ...restJson } = manifestJson || {}; const isAdminPortal = (0, lowcode_builder_1.buildAsAdminPortalByBuildType)(webOptions.buildTypeList); const defaultCdnDomain = domains[0]; preHeatUrls = preHeatUrls.map((path) => { var _a; /** * 因此当没有域名的情况下,直接根据当前的域名来使用 */ const domain = ((_a = webOptions.gatewayConfig) === null || _a === void 0 ? void 0 : _a.domain) || defaultCdnDomain; return path.startsWith('/') ? `${RUNTIME_PROTOCOL}://${domain}${path}` : path; }); if (isAdminPortal) { preHeatUrls.push(`${RUNTIME_PROTOCOL}://${defaultCdnDomain}/adminportal/`); if ((_a = webOptions === null || webOptions === void 0 ? void 0 : webOptions.gatewayConfig) === null || _a === void 0 ? void 0 : _a.domain) { preHeatUrls.push(`${RUNTIME_PROTOCOL}://${(_b = webOptions === null || webOptions === void 0 ? void 0 : webOptions.gatewayConfig) === null || _b === void 0 ? void 0 : _b.domain}/adminportal/`); } if (version) { preHeatUrls.push(`${RUNTIME_PROTOCOL}://${defaultCdnDomain}${webOptions.cloudPath}?version=${version}`); } } fs_extra_1.default.writeFileSync(MAINIFEST_PATH, JSON.stringify({ ...restJson, preHeatUrls }, undefined, 2)); } return { domains, website: currentWebsite }; } async _postProcessWebDeploy({ domains = [] }) { const { mode, appId, envId, webOptions, outputPath } = this.resolvedInputs; if ((0, lowcode_builder_1.buildAsAdminPortalByBuildType)(webOptions.buildTypeList)) { await this._postProcessAdminPortal(); } else { await this._postProcessWebsiteConfig(domains); } const cdnDomain = webOptions.gatewayConfig ? webOptions.gatewayConfig.domain : domains[0]; const cdnPath = webOptions.gatewayConfig ? webOptions.gatewayConfig.path : webOptions.cloudPath; try { const isPreview = mode === EDeployMode.PREVIEW; const link = (0, lowcode_builder_1.buildAsAdminPortalByBuildType)(webOptions.buildTypeList) ? `${RUNTIME_PROTOCOL}://${cdnDomain}/adminportal/#/app/${isPreview ? `${appId}-preview` : appId}?envType=${isPreview ? 'preview' : 'prod'}` : `${RUNTIME_PROTOCOL}://${cdnDomain + cdnPath}`; const qrcodeOutputPath = path_1.default.resolve(outputPath, QRCODE_PATH); await qrcode_1.default.toFile(qrcodeOutputPath, link, { errorCorrectionLevel: 'M', type: 'image/jpeg', scale: 12, margin: 2, }); this.logger.info(`${this.api.emoji('🚀')} 网站部署成功:${link}`); this.logger.info(`${this.api.emoji('🚀')} 网站部署成功, 访问二维码地址:${url_1.default.format({ protocol: 'file:', host: qrcodeOutputPath, })}`); return { link, qrCode: qrcodeOutputPath }; } catch (e) { this.logger.error('网站部署失败: ', e); throw e; } } async _postProcessAdminPortal() { if (this.isPrivate) { return; } try { await this.lowcodeService.request('PublishAppCustomUrl', { EnvId: this.resolvedInputs.envId, WeAppId: this.resolvedInputs.appId, EnvType: this.resolvedInputs.mode === EDeployMode.PREVIEW ? 'pre' : 'prod', }); this.logger.debug('PublishAppCustomUrl成功'); } catch (e) { this.logger.error('PublishAppCustomUrl失败: ', e); } } async _postProcessWebsiteConfig(domains) { if (this.isPrivate) { return; } const { mode, appId, envId, webOptions } = this.resolvedInputs; const hostingService = this.cloudbaseManager.hosting; try { let { Domains: domainList } = await hostingService.tcbCheckResource({ domains, }); // hostingService.getInfo(); await Promise.all(domainList .filter((item) => item.DomainConfig.FollowRedirect !== 'on') .map((item) => hostingService.tcbModifyAttribute({ domain: item.Domain, domainId: item.DomainId, domainConfig: { FollowRedirect: 'on' }, }))); await Promise.all([ this.lowcodeService .request('PublishAppCustomUrl', { EnvId: envId, WeAppId: appId, IndexPath: webOptions.cloudPath, EnvType: mode === EDeployMode.PREVIEW ? 'pre' : 'prod', }) .catch((e) => { this.logger.error('自定义域名发布失败: ', e); if ((e === null || e === void 0 ? void 0 : e.code) !== 'ResourceNotFound') { throw e; } }), ]); } catch (e) { this.logger.error('网站路由注册失败: ', e); throw e; } } async _getHostingInfoTask(envId, loose = false) { let timeout = null; let [website, hostingDatas] = await Promise.race([ new Promise((resolve) => { timeout = setTimeout(() => { resolve([]); }, 300 * 1000); }), this._getHostingInfo(envId, loose), ]); if (timeout) { clearTimeout(timeout); } return [website, hostingDatas]; } async _getHostingInfo(envId, loose = false) { let [website, hostingDatas] = await this.tcbService .request('DescribeStaticStore', { EnvId: envId }) .then(({ Data: hostingDatas }) => { let website = hostingDatas[0]; return [website, hostingDatas]; }); // 此处采取松校验 if (!website || (loose ? false : (website === null || website === void 0 ? void 0 : website.Status) !== 'online')) { await new Promise((resolve) => { setTimeout(() => { resolve(true); }, 8 * 1000); }); return this._getHostingInfo(envId, loose); } else { return [website, hostingDatas]; } } _generateLowcodeService(credential) { var _a, _b, _c, _d, _e; const params = { service: 'lowcode', version: '2021-01-08', timeout: 10000, }; return this.isPrivate ? this.credential.weda ? new tcb_api_capi_1.TCBAPICloudApiService({ ...params, env: this.resolvedInputs.envId, apiOrigin: ``, endpoint: process.env.PRIVATE_ENDPOINT || `http://${(_b = (_a = this.resolvedInputs) === null || _a === void 0 ? void 0 : _a.webOptions) === null || _b === void 0 ? void 0 : _b.domain}`, uin: (_c = this.account) === null || _c === void 0 ? void 0 : _c.uin, username: (_d = this.credential.weda) === null || _d === void 0 ? void 0 : _d.username, password: (_e = this.credential.weda) === null || _e === void 0 ? void 0 : _e.password, localstorage: this._localstorage, }) : new cloud_api_service_1.TCECloudApiService({ ...params, credential, baseUrl: process.env.PRIVATE_CLOUD_API_DOMAIN || 'yunapi3.weda-private.tcs230.fsphere.cn', endpoint: process.env.PRIVATE_ENDPOINT || '', }) : new cloud_api_1.CloudApiService({ ...params, credential }); } _generateTcbService(credential) { var _a, _b, _c, _d, _e; const params = { service: 'tcb', version: '2018-06-08', credential, }; return this.isPrivate ? this.credential.weda ? new tcb_api_capi_1.TCBAPICloudApiService({ ...params, env: this.resolvedInputs.envId, apiOrigin: ``, endpoint: process.env.PRIVATE_ENDPOINT || `http://${(_b = (_a = this.resolvedInputs) === null || _a === void 0 ? void 0 : _a.webOptions) === null || _b === void 0 ? void 0 : _b.domain}`, uin: (_c = this.account) === null || _c === void 0 ? void 0 : _c.uin, username: (_d = this.credential.weda) === null || _d === void 0 ? void 0 : _d.username, password: (_e = this.credential.weda) === null || _e === void 0 ? void 0 : _e.password, localstorage: this._localstorage, }) : new cloud_api_service_1.TCECloudApiService({ ...params, baseUrl: process.env.PRIVATE_CLOUD_API_DOMAIN || 'yunapi3.weda-private.tcs230.fsphere.cn', endpoint: process.env.PRIVATE_ENDPOINT || '', }) : new cloud_api_1.CloudApiService(params); } _generateMpCIProject({ mpAppId: appid, projectPath, privateKeyPath, ignores }) { return new miniprogram_ci_1.Project({ appid, type: 'miniProgram', projectPath, privateKeyPath, ignores, }); } /** * 小程序-编译NPM */ async genearteMiniprogramNPM() { // 需要暂时关掉 stdout, 避免 miniprogram-ci 的内容打印到控制台 // this.api.runtime.isLocal() && this.api.console.pause(); !process.env.CLOUDBASE_CIID && pauseConsoleOutput(); const result = await (0, miniprogram_ci_1.packNpm)(this.mpCIProject, { reporter: (infos) => { console.log(infos); }, }).catch((err) => { return err; }); // this.api.runtime.isLocal() && this.api.console.resume(); !process.env.CLOUDBASE_CIID && resumeConsoleOutput(); if (result instanceof Error && !result.message.startsWith(NOT_NPM_ERROR)) { throw new Error(`小程序 NPM 构建失败 ${result}`); } } async _cleanNonRefFile() { const analyseRes = await (0, miniprogram_ci_1.analyseCode)(this.mpCIProject, { silent: true }); if (analyseRes) { const codeExtensions = new Set(['.wxml', '.wxss', '.wxs', '.js', '.json', '.json', '.ts']); const { files } = analyseRes; const filesToDelete = files .filter((f) => { var _a, _b; if ((codeExtensions.has(f.ext) && !f.moduleId && (((_b = (_a = f.path) === null || _a === void 0 ? void 0 : _a.startsWith) === null || _b === void 0 ? void 0 : _b.call(_a, 'materials/')) || /^miniprogram_npm\/(crypto-js|@cloudbase|buffer|lodash)/.test(f.path || ''))) || /^packages\/\$wd_system\/miniprogram_npm\/(crypto-js|@cloudbase\/js-sdk\/index.js|@cloudbase\/js-sdk\/database.js|@cloudbase\/js-sdk\/realtime.js|@cloudbase\/js-sdk\/ai.js|@cloudbase\/js-sdk\/auth.js|@cloudbase\/utilities|@cloudbase\/database|@cloudbase\/realtime|bson|buffer)/.test(f.path || '')) { return true; } return false; }) .map((f) => f.path); return Promise.all(filesToDelete.map(async (f) => { await fs_extra_1.default.remove(path_1.default.join(this.mpCIProject.projectPath, f)); return f; })); } else { throw new Error('小程序代码静态分析失败'); } } /** * 小程序-预览 */ async _ciPreview(options = {}) { var _a; const logger = this.logger; // 需要暂时关掉 stdout, 避免 miniprogram-ci 的内容打印到控制台 // this.api.runtime.isLocal() && this.api.console.pause(); !process.env.CLOUDBASE_CIID && pauseConsoleOutput(); const previewOptions = { version: '0.0.1', desc: 'CloudBase Framework 一键预览', pagePath: 'pages/index/index', searchQuery: '', scene: 1011, qrcodeFormat: 'image', qrcodeOutputPath: './qrcode.jpg', ...(((_a = this.resolvedInputs.miniprogramOptions) === null || _a === void 0 ? void 0 : _a.previewOptions) || {}), ...options, }; const qrcodeOutputDest = path_1.default.resolve(this.resolvedInputs.outputPath, previewOptions.qrcodeOutputPath); const result = await (0, miniprogram_ci_1.preview)({ project: this.mpCIProject, ...previewOptions, qrcodeOutputDest, onProgressUpdate: (res) => onProgressUpdate(logger, res), }).catch((err) => { return err; }); // this.api.runtime.isLocal() && this.api.console.resume(); !process.env.CLOUDBASE_CIID && resumeConsoleOutput(); if (result === null || result === void 0 ? void 0 : result.subPackageInfo) { return { link: qrcodeOutputDest, }; } else { console.log(result); throw new Error(`小程序(预览版)部署失败 ${result.message}`); } } /** * 小程序-上传 */ async _ciUpload(options = {}) { const logger = this.logger; // 需要暂时关掉 stdout, 避免 miniprogram-ci 的内容打印到控制台 !process.env.CLOUDBASE_CIID && pauseConsoleOutput(); const uploadOptions = { version: '1.0.0', desc: 'CloudBase Framework 一键上传', ...(this.resolvedInputs.miniprogramOptions.uploadOptions || {}), ...(options || {}), }; const result = await (0, miniprogram_ci_1.upload)({ project: this.mpCIProject, ...uploadOptions, onProgressUpdate: (res) => onProgressUpdate(logger, res), }).catch((err) => { return err; }); !process.env.CLOUDBASE_CIID && resumeConsoleOutput(); if (result === null || result === void 0 ? void 0 : result.subPackageInfo) { return { version: uploadOptions.version, }; } else { throw new Error(`小程序(体验版)部署失败 ${result.message}`); } } } exports.default = LowcodeDeployer; exports.deployer = LowcodeDeployer; const originalStdoutWrite = process.stdout.write.bind(process.stdout); const originalStderrWrite = process.stderr.write.bind(process.stderr); let previousStdoutWrite = process.stdout.write.bind(process.stdout); let previousStderrWrite = process.stderr.write.bind(process.stderr); // 暂停控制台输出 function pauseConsoleOutput() { previousStdoutWrite = process.stdout.write.bind(process.stdout); process.stdout.write = () => { return true; }; previousStderrWrite = process.stderr.write.bind(process.stderr); process.stderr.write = () => { return true; }; } // 恢复控制台输出 function resumeConsoleOutput(original = false) { if (original) { process.stdout.write = originalStdoutWrite; process.stderr.write = originalStderrWrite; } else { process.stdout.write = previousStdoutWrite; process.stderr.write = previousStderrWrite; } } function zipDir(src, dist) { return new Promise((resolve, reject) => { // create a file to stream archive data to. let output = fs_extra_1.default.createWriteStream(dist); let archive = (0, archiver_1.default)('zip', { zlib: { level: 9 }, // Sets the compression level. }); output.on('close', resolve); archive.on('error', reject); archive.glob('**/*', { cwd: src, ignore: [dist], dot: true, }, {}); archive.pipe(output); archive.finalize(); }); } function onProgressUpdate(logger, res) { if (res.status === 'done' || res.status === 'Compiling' || res.status === 'compiling') { return; } else { logger.debug(res); } }