UNPKG

@topgroup/diginext

Version:

A BUILD SERVER & CLI to deploy apps to any Kubernetes clusters.

185 lines (184 loc) 10.2 kB
"use strict"; 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;