@topgroup/diginext
Version:
A BUILD SERVER & CLI to deploy apps to any Kubernetes clusters.
185 lines (184 loc) • 10.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildAndDeploy = void 0;
const chalk_1 = __importDefault(require("chalk"));
const dayjs_1 = __importDefault(require("dayjs"));
const humanize_duration_1 = __importDefault(require("humanize-duration"));
const app_config_1 = require("../../app.config");
const plugins_1 = require("../../plugins");
const cloud_storage_1 = require("../../plugins/cloud-storage");
const mongodb_1 = require("../../plugins/mongodb");
const server_1 = require("../../server");
const MediaService_1 = __importDefault(require("../../services/MediaService"));
const screenshot_1 = __importDefault(require("../capture/screenshot"));
const create_build_slug_1 = require("../deploy/create-build-slug");
const deploy_build_v2_1 = require("../deploy/deploy-build-v2");
const build_1 = require("./build");
const send_log_message_1 = require("./send-log-message");
const buildAndDeploy = async (buildParams, deployParams) => {
var _a, _b;
// import services
const { AppService } = await Promise.resolve().then(() => __importStar(require("../../services")));
const { BuildService } = await Promise.resolve().then(() => __importStar(require("../../services")));
const { ReleaseService } = await Promise.resolve().then(() => __importStar(require("../../services")));
const appSvc = new AppService();
const buildSvc = new BuildService();
const releaseSvc = new ReleaseService();
// [1] Build container image
if (typeof buildParams.buildWatch === "undefined")
buildParams.buildWatch = true;
buildParams.env = deployParams.env;
buildParams.shouldDeploy = true; // <-- keep this to disable webhook notification when build success
let buildInfo;
try {
buildInfo = await (0, build_1.startBuild)(buildParams);
if (!buildInfo)
throw new Error(`[BUILD_AND_DEPLOY] Unable to build.`);
}
catch (e) {
// build failed -> stop build
const app = await appSvc.findOne({ slug: buildParams.appSlug }, { populate: ["workspace"] });
const SOCKET_ROOM = (0, create_build_slug_1.createBuildSlug)({ projectSlug: app.projectSlug, appSlug: buildParams.appSlug, buildTag: buildParams.buildTag });
(0, build_1.stopBuild)(app.projectSlug, app.slug, SOCKET_ROOM, "failed");
(0, send_log_message_1.sendLog)({ SOCKET_ROOM, type: "error", message: `Build error: ${e.stack}` });
// AI analysis: get latest 100 lines of container logs
const build = await buildSvc.findOne({ slug: SOCKET_ROOM });
const fullLogs = build === null || build === void 0 ? void 0 : build.logs;
const latestLogs = fullLogs ? fullLogs.split("\n").slice(-100).join("\n") : undefined;
const workspace = app.workspace;
const owner = buildParams.user;
console.log("buildAndDeploy() > latestLogs :>> ", latestLogs);
console.log("buildAndDeploy() > workspace :>> ", workspace);
console.log("buildAndDeploy() > owner :>> ", owner);
let aiAnalysis = "\n---- AI ANALYSIS ----\n";
if (((_b = (_a = workspace.settings) === null || _a === void 0 ? void 0 : _a.ai) === null || _b === void 0 ? void 0 : _b.enabled) && latestLogs && build) {
const { AIService } = await Promise.resolve().then(() => __importStar(require("../../services/AIService")));
const aiService = new AIService({ owner, workspace });
try {
const analysis = await aiService.analyzeErrorLog(latestLogs, { isDebugging: buildParams.isDebugging });
aiAnalysis += analysis;
}
catch (error) {
console.error(error);
aiAnalysis += `AI service is currently unavailable: ${error.message}`;
}
(0, send_log_message_1.sendLog)({ SOCKET_ROOM, type: "log", message: aiAnalysis });
}
else {
(0, send_log_message_1.sendLog)({ SOCKET_ROOM, type: "log", message: "AI analysis is disabled. If you need it, please enable it in your workspace settings." });
}
return;
}
const { build, startTime, SOCKET_ROOM } = buildInfo;
(0, send_log_message_1.sendLog)({ SOCKET_ROOM, message: `[BUILD_AND_DEPLOY] Finished building > buildTag :>> ${build.tag}` });
(0, send_log_message_1.sendLog)({ SOCKET_ROOM, message: `[BUILD_AND_DEPLOY] Finished building > buildNumber :>> ${build.num}` });
if (!build)
throw new Error(`Unable to build "${buildParams.appSlug}" app (${buildParams.env}).`);
const { appSlug, projectSlug } = build;
// [2] Deploy the build to target deploy environment
if (!deployParams.env)
deployParams.env = buildParams.env || "dev";
if (typeof deployParams.deployInBackground === "undefined")
deployParams.deployInBackground = false;
let deployRes;
try {
deployRes = await (0, deploy_build_v2_1.deployBuildV2)(build, deployParams);
}
catch (e) {
(0, build_1.stopBuild)(projectSlug, appSlug, SOCKET_ROOM, "success", "failed");
(0, send_log_message_1.sendLog)({ SOCKET_ROOM, type: "error", message: `Deploy error: ${e.stack}` });
return;
}
const { env } = deployParams;
const { release, deployment } = deployRes;
(0, send_log_message_1.sendLog)({ SOCKET_ROOM, message: `[BUILD_AND_DEPLOY] Finished building > Release ID :>> ${release._id}` });
const releaseId = mongodb_1.MongoDB.toString(release._id);
const { endpoint } = deployment;
// [3] Print success information
const endTime = (0, dayjs_1.default)();
const buildDuration = endTime.diff(startTime, "millisecond");
const humanDuration = (0, humanize_duration_1.default)(buildDuration, { round: true });
(0, send_log_message_1.sendLog)({ SOCKET_ROOM, message: chalk_1.default.green(`🎉 FINISHED DEPLOYING AFTER ${humanDuration} 🎉`), type: "success" });
// [4] Capture a screenshot (scheduled after 30 seconds after the deployment):
// console.log("IsTestCI() :>> ", IsTestCI());
// console.log("Config.ENV :>> ", Config.ENV);
// console.log("process.env.NODE_ENV :>> ", process.env.NODE_ENV);
if (!(0, app_config_1.IsTest)()) {
try {
// let's this job run in background after 2 minutes
(0, plugins_1.wait)(2 * 60 * 1000, async () => {
const result = await (0, screenshot_1.default)(endpoint, { fullPage: false });
if (result) {
// upload to cloud storage (if any)
const { workspace } = deployParams;
let cloudUploadedUrl;
if (workspace && workspace.settings.cloud_storage) {
const uploaded = await (0, cloud_storage_1.uploadFileBuffer)(result.buffer, result.name, {
storage: workspace.settings.cloud_storage,
}).catch((e) => {
console.error(`[BUILD_AND_DEPLOY] Unable to upload screenshot to cloud storage (${endpoint}): ${e}`);
return null;
});
if (uploaded)
cloudUploadedUrl = uploaded.publicUrl;
}
// success -> write to db
delete result.buffer;
const mediaSvc = new MediaService_1.default({ owner: deployParams.owner, workspace: deployParams.workspace });
const media = await mediaSvc.create({
...result,
screenshotUrl: cloudUploadedUrl || result.url,
owner: deployParams.owner._id,
workspace: deployParams.workspace._id,
});
if (media) {
// update screenshot to release
releaseSvc.updateOne({ _id: releaseId }, { screenshot: media.url });
// update screenshot to app's deploy environment
appSvc.updateOne({ slug: appSlug }, { [`deployEnvironment.${env}.screenshot`]: media.url });
}
}
});
}
catch (e) {
(0, send_log_message_1.sendLog)({
SOCKET_ROOM,
message: `[BUILD_AND_DEPLOY] Unable to capture the webpage screenshot (${endpoint}): ${e}`,
});
}
}
// [5] Send success message to the CLI client:
(0, send_log_message_1.sendLog)({ SOCKET_ROOM, message: chalk_1.default.bold(chalk_1.default.yellow(`✓ Check out your release at: ${endpoint}`)), type: "success" });
// wait for 3 seconds before disconnecting the CLI client
await (0, plugins_1.wait)(3000);
// disconnect CLI client:
server_1.socketIO === null || server_1.socketIO === void 0 ? void 0 : server_1.socketIO.to(SOCKET_ROOM).emit("message", { action: "end" });
return { build, release };
};
exports.buildAndDeploy = buildAndDeploy;