@cloudbase/lowcode-deployer
Version:
deploy weda app
843 lines (842 loc) • 40.7 kB
JavaScript
"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||buffer|lodash)/.test(f.path || ''))) ||
/^packages\/\$wd_system\/miniprogram_npm\/(crypto-js|\/js-sdk\/index.js|\/js-sdk\/database.js|\/js-sdk\/realtime.js|\/js-sdk\/ai.js|\/js-sdk\/auth.js|\/utilities|\/database|\/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);
}
}