UNPKG

@topgroup/diginext

Version:

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

539 lines (538 loc) 28.5 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.rolloutV2 = exports.cleanUpNamespace = void 0; const chalk_1 = __importDefault(require("chalk")); const log_1 = require("diginext-utils/dist/xconsole/log"); const fs_1 = require("fs"); const js_yaml_1 = __importDefault(require("js-yaml")); const lodash_1 = require("lodash"); const path_1 = __importDefault(require("path")); const app_config_1 = require("../../app.config"); const const_1 = require("../../config/const"); const k8s_1 = __importDefault(require("../../modules/k8s")); const kubectl_1 = require("../../modules/k8s/kubectl"); const plugins_1 = require("../../plugins"); const mongodb_1 = require("../../plugins/mongodb"); const slug_1 = require("../../plugins/slug"); const services_1 = require("../../services"); const generate_deployment_name_1 = __importDefault(require("./generate-deployment-name")); const mark_release_as_active_1 = require("./mark-release-as-active"); const deployReplicas = 2; const checkDeploymentReady = async (options) => { const { namespace, appName, appVersion, replicas = 1, onUpdate, isDebugging = false, context } = options; if (isDebugging) (0, log_1.log)(`checkDeploymentReady() :>>`, options); const filterLabel = `main-app=${appName}${appVersion ? `,app-version=${appVersion}` : ""}`; let pods = await k8s_1.default.getPods(namespace, { context, filterLabel, metrics: false, isDebugging, skipOnError: true, }).catch((e) => { (0, log_1.logError)(`[CHECK DEPLOYMENT READY] Error: ${e}`); return []; }); // Skip crashed pods if (options.skipCrashedPods) { console.log("deploy-rollout.ts > checkDeploymentReady() > pods before :>>", pods); pods = pods.filter((pod) => !pod.status.containerStatuses.some((containerStatus) => { var _a; return ((_a = containerStatus.state.waiting) === null || _a === void 0 ? void 0 : _a.reason) === "CrashLoopBackOff"; })); console.log("deploy-rollout.ts > checkDeploymentReady() > pods after :>>", pods); } if (!pods || pods.length == 0) { const msg = `Unable to check "${appName}" deployment:\n- Namespace: ${namespace}\n- Context: ${context}\n- Reason: Selected pods not found: ${filterLabel}.`; if (onUpdate) onUpdate(msg); return false; } let isReady = false; let countReady = 0; // if (isDebugging) log(`[ROLL OUT V2] "${appVersion}" > checking ${pods.length} pods > pod.status.conditions:`); try { pods.forEach((pod) => { var _a, _b, _c, _d, _e, _f; if (isDebugging) (0, log_1.log)((_b = (_a = pod.status) === null || _a === void 0 ? void 0 : _a.conditions) === null || _b === void 0 ? void 0 : _b.map((c) => `- ${c.type}: ${c.status} (Reason: "${c.reason}")`).join("\n")); (_d = (_c = pod.status) === null || _c === void 0 ? void 0 : _c.conditions) === null || _d === void 0 ? void 0 : _d.filter((condition) => condition.type === "Ready").map((condition) => { if (condition.status === "True") countReady++; }); (_f = (_e = pod.status) === null || _e === void 0 ? void 0 : _e.containerStatuses) === null || _f === void 0 ? void 0 : _f.map((containerStatus) => { if (containerStatus.restartCount > 0) throw new Error(`App is unable to start up due to some unexpected errors.`); }); }); } catch (e) { if (onUpdate) onUpdate(e.message); return false; } if (countReady >= replicas) isReady = true; // notify to the dashboard: const msg = `Checking is "${appName}" deployment ready (${countReady}/${replicas}): ${isReady}`; (0, log_1.log)(msg); if (onUpdate) onUpdate(msg); return isReady; }; /** * Clean up namespace's resources by app version * @param cluster - Cluster * @param appVersion - App's version */ async function cleanUpNamespace(cluster, namespace, appName, appVersion) { const { contextName: context } = cluster; // Clean up Prerelease YAML const cleanUpCommands = []; // Delete INGRESS to optimize cluster cleanUpCommands.push(k8s_1.default.deleteIngressByFilter(namespace, { context, skipOnError: true, filterLabel: `main-app=${appName},app-version!=${appVersion}`, })); // Delete Prerelease SERVICE to optimize cluster cleanUpCommands.push(k8s_1.default.deleteServiceByFilter(namespace, { context, filterLabel: `main-app=${appName},app-version!=${appVersion}` })); // Clean up Prerelease Deployments cleanUpCommands.push(k8s_1.default.deleteDeploymentsByFilter(namespace, { context, filterLabel: `main-app=${appName},app-version!=${appVersion}` })); // Clean up immediately & just ignore if any errors let data; for (const cmd of cleanUpCommands) { try { data = await cmd; } catch (e) { (0, log_1.logWarn)(`[CLEAN UP] Ignore command: ${e}`); } } // * Print success: let msg = `🎉 NAMESPACE HAS BEEN CLEANED UP SUCCESSFULLY 🎉`; (0, log_1.logSuccess)(msg); return { error: null, data }; } exports.cleanUpNamespace = cleanUpNamespace; /** * Roll out a release (V2) * @param releaseId - Release ID */ async function rolloutV2(releaseId, options = {}) { const { DB } = await Promise.resolve().then(() => __importStar(require("../../modules/api/DB"))); const { onUpdate } = options; let releaseData = await DB.updateOne("release", { _id: releaseId }, { status: "in_progress" }, { populate: ["owner", "workspace"] }); if ((0, lodash_1.isEmpty)(releaseData)) { const error = `Unable to roll out: Release "${releaseId}" not found.`; if (onUpdate) onUpdate(error); return { error }; } const { slug: releaseSlug, projectSlug, // ! This is not PROJECT_ID of Google Cloud provider cluster: clusterSlug, appSlug, build: buildId, buildNumber, deploymentYaml, endpoint: endpointUrl, namespace, env, message, } = releaseData; // webhook const owner = releaseData.owner; const workspace = releaseData.workspace; const webhookSvc = new services_1.WebhookService(); webhookSvc.ownership = { owner, workspace }; const webhook = await DB.findOne("webhook", { release: releaseId }); // log(`Rolling out the release: "${releaseSlug}" (ID: ${id})`); if (onUpdate) onUpdate(`Rolling out the release: "${releaseSlug}" (ID: ${releaseId})`); // get the app const app = await DB.findOne("app", { slug: appSlug }, { populate: ["project"] }); if (!app && onUpdate) { const error = `Unable to roll out: app "${appSlug}" not found.`; onUpdate(error); // update release as "failed" await DB.update("release", { _id: releaseId }, { status: "failed" }, { select: ["_id", "status"] }).catch(console.error); // Update "deployStatus" of a build to success await DB.update("build", { _id: buildId }, { deployStatus: "failed" }, { select: ["_id", "deployStatus"] }).catch(console.error); return { error }; } // log(`Rolling out > app:`, app); const deprecatedMainAppName = (0, slug_1.makeSlug)(app === null || app === void 0 ? void 0 : app.name).toLowerCase(); const mainAppName = await (0, generate_deployment_name_1.default)(app); const deployEnvironment = app.deployEnvironment[env]; /** * App's version (for service & deployment selector) */ const appVersion = releaseData.appVersion || `${mainAppName}-${buildNumber}`; // log(`Rolling out > mainAppName:`, mainAppName); // authenticate cluster's provider & switch kubectl to that cluster: const cluster = await DB.findOne("cluster", { slug: clusterSlug }, { subpath: "/all" }); if (!cluster) { (0, log_1.logError)(`Cluster "${clusterSlug}" not found.`); // dispatch/trigger webhook if (webhook) webhookSvc.trigger(mongodb_1.MongoDB.toString(webhook._id), "failed"); // update release as "failed" await DB.update("release", { _id: releaseId }, { status: "failed" }, { select: ["_id", "status"] }).catch(console.error); // Update "deployStatus" of a build to success await DB.update("build", { _id: buildId }, { deployStatus: "failed" }, { select: ["_id", "deployStatus"] }).catch(console.error); return { error: `Cluster "${clusterSlug}" not found.` }; } try { await k8s_1.default.authCluster(cluster, { ownership: { owner, workspace } }); // log(`Rolling out > Checked connectivity of "${clusterSlug}" cluster.`); } catch (e) { const error = `Unable to authenticate the cluster: ${e.message}`; (0, log_1.logError)(`[ROLL_OUT] ${error}`); // dispatch/trigger webhook if (webhook) webhookSvc.trigger(mongodb_1.MongoDB.toString(webhook._id), "failed"); // update release as "failed" await DB.update("release", { _id: releaseId }, { status: "failed" }, { select: ["_id", "status"] }).catch(console.error); // Update "deployStatus" of a build to success await DB.update("build", { _id: buildId }, { deployStatus: "failed" }, { select: ["_id", "deployStatus"] }).catch(console.error); return { error }; } const { contextName: context } = cluster; if (options === null || options === void 0 ? void 0 : options.isDebugging) (0, log_1.log)(`Rolling out > Connected to "${clusterSlug}" cluster.`); // create temporary directory to store release's yaml const tmpDir = path_1.default.resolve(const_1.CLI_DIR, `storage/releases/${releaseSlug}`); if (!(0, fs_1.existsSync)(tmpDir)) (0, fs_1.mkdirSync)(tmpDir, { recursive: true }); // ! NEW WAY -> LESS DOWNTIME WHEN ROLLING OUT NEW DEPLOYMENT ! /** * Check if there is any prod namespace, if not -> create one */ const isNsExisted = await k8s_1.default.isNamespaceExisted(namespace, { context }); if (!isNsExisted) { (0, log_1.log)(`Namespace "${namespace}" not found, creating one...`); if (onUpdate) onUpdate(`Namespace "${namespace}" not found, creating one...`); const createNsRes = await k8s_1.default.createNamespace(namespace, { context }); if (!createNsRes) { const err = `Unable to create new namespace: ${namespace} (Cluster: ${clusterSlug} / Namespace: ${namespace} / App: ${appSlug} / Env: ${env})`; (0, log_1.logError)(`[ROLL_OUT]`, err); if (onUpdate) onUpdate(err); // dispatch/trigger webhook if (webhook) webhookSvc.trigger(mongodb_1.MongoDB.toString(webhook._id), "failed"); // update release as "failed" await DB.update("release", { _id: releaseId }, { status: "failed" }, { select: ["_id", "status"] }).catch(console.error); // Update "deployStatus" of a build to success await DB.update("build", { _id: buildId }, { deployStatus: "failed" }, { select: ["_id", "deployStatus"] }).catch(console.error); return { error: err }; } } // create "imagePullSecret" in namespace: try { const { name: imagePullSecretName } = await k8s_1.default.createImagePullSecretsInNamespace(appSlug, env, clusterSlug, namespace); if (onUpdate) onUpdate(`[ROLL OUT V2] Created "${imagePullSecretName}" imagePullSecrets in the "${namespace}" namespace (cluster: "${clusterSlug}").`); } catch (e) { const error = `[ROLL OUT V2] Can't create "imagePullSecrets" in the "${namespace}" namespace (cluster: "${clusterSlug}").`; // dispatch/trigger webhook if (webhook) webhookSvc.trigger(mongodb_1.MongoDB.toString(webhook._id), "failed"); // update release as "failed" await DB.update("release", { _id: releaseId }, { status: "failed" }, { select: ["_id", "status"] }).catch(console.error); // Update "deployStatus" of a build to success await DB.update("build", { _id: buildId }, { deployStatus: "failed" }, { select: ["_id", "deployStatus"] }).catch(console.error); return { error }; } /** * Start applying new deployment YAML */ if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("[ROLL OUT V2] Deployment YAML :>> ", deploymentYaml); let newReplicas = 1, currentReplicas = 0, currentDeploymentName, currentAppVersion, envVars = [], resourceQuota = {}, service, svcName, ingress, ingressName, deployment, deploymentName; let deploymentCfg = js_yaml_1.default.loadAll(deploymentYaml); if (deploymentCfg.length > 1) { deploymentCfg.forEach((doc) => { if (doc && doc.kind == "Ingress") { ingress = doc; ingressName = doc.metadata.name; } if (doc && doc.kind == "Service") { service = doc; svcName = doc.metadata.name; } if (doc && doc.kind == "Deployment") { newReplicas = doc.spec.replicas; // important: set new deployment's replicas to "deployReplicas" for temporary -> set back later (avoid downtime) doc.spec.replicas = deployReplicas; // envVars = doc.spec.template.spec.containers[0].env; resourceQuota = doc.spec.template.spec.containers[0].resources; deployment = doc; deploymentName = doc.metadata.name; } }); } const tmpDeploymentYaml = (0, plugins_1.objectToDeploymentYaml)(deploymentCfg); const [currentDeployment] = await k8s_1.default.getDeploys(namespace, { context, filterLabel: `main-app=${mainAppName}`, metrics: false }); currentReplicas = currentDeployment && typeof currentDeployment !== "string" ? currentDeployment.spec.replicas : 1; currentDeploymentName = currentDeployment && typeof currentDeployment !== "string" ? currentDeployment.metadata.name : ""; currentAppVersion = currentDeployment && typeof currentDeployment !== "string" ? currentDeployment.metadata.labels["app-version"] : undefined; const currentPods = await k8s_1.default.getPods(namespace, { context, filterLabel: `main-app=${mainAppName}`, metrics: false }); const countCrashedPods = currentPods.filter((pod) => pod.status.containerStatuses.find((containerStatus) => { var _a; return ((_a = containerStatus.state.waiting) === null || _a === void 0 ? void 0 : _a.reason) === "CrashLoopBackOff"; }) !== undefined).length; const countRunningPods = currentPods.filter((pod) => pod.status.phase === "Running").length; // check ingress domain has been used yet or not: let isDomainUsed = false, usedDomain, usedDomainNamespace, deleteIng; if (ingress) { const domains = ingress.spec.rules.map((rule) => rule.host) || []; // console.log("domains :>> ", domains); if (domains.length > 0) { const allIngresses = await k8s_1.default.getAllIngresses({ context }); allIngresses.filter((ing) => { domains.map((domain) => { if (ing.spec.rules.map((rule) => rule.host).includes(domain) && ing.metadata.namespace !== namespace) { isDomainUsed = true; usedDomain = domain; deleteIng = ing; usedDomainNamespace = ing.metadata.namespace; } }); }); if (isDomainUsed) { // await ClusterManager.deleteIngress(deleteIng.metadata.name, deleteIng.metadata.namespace, { context }); // if (onUpdate) // onUpdate( // `Domain "${usedDomain}" has been used before at "${deleteIng.metadata.namespace}" namespace -> Deleted "${deleteIng.metadata.name}" ingress to create a new one.` // ); const error = `This domain "${service.metadata.name}" has been using in "${usedDomainNamespace}" namespace.`; if (onUpdate) onUpdate(error); // dispatch/trigger webhook if (webhook) webhookSvc.trigger(mongodb_1.MongoDB.toString(webhook._id), "failed"); // update release as "failed" await DB.update("release", { _id: releaseId }, { status: "failed" }, { select: ["_id", "status"] }).catch(console.error); // Update "deployStatus" of a build to success await DB.update("build", { _id: buildId }, { deployStatus: "failed" }, { select: ["_id", "deployStatus"] }).catch(console.error); return { error }; } } } /** * Scale current deployment up to many replicas before apply new deployment YAML * But if there are many crashed pods -> skip scaling up */ if (newReplicas === 1 && countCrashedPods === 0 && countRunningPods === 1) { if (currentDeploymentName) { if (onUpdate) onUpdate(`Scaling "${currentDeploymentName}" deployment to ${deployReplicas} & prepare for rolling out new deployment.`); await k8s_1.default.scaleDeploy(currentDeploymentName, deployReplicas, namespace, { context }); // wait 10 secs // await wait(30 * 1000); // await ClusterManager.setDeployImageAll(deploymentName, `${deployEnvironment.imageURL}:${deployEnvironment.buildTag}`, namespace, { context }); // wait until an old deployment has been scaled successfully const isDeploymentFinishScaling = await (0, plugins_1.waitUntil)(() => checkDeploymentReady({ context, appName: mainAppName, namespace, replicas: deployReplicas, onUpdate, // isDebugging: true, }), 5, 5 * 60).catch((e) => false); if (!isDeploymentFinishScaling) { (0, log_1.logWarn)(`Unable to scale up the previous deployment, downtime might happen.`); if (onUpdate) onUpdate(`Unable to scale up the previous deployment (${currentDeploymentName}) to ${deployReplicas} replicas, downtime might happen.`); } else { if (onUpdate) onUpdate(`Scaled "${currentDeploymentName}" deployment to ${deployReplicas} replicas successfully.`); } } } /** * Apply new deployment yaml */ try { await k8s_1.default.kubectlApplyContent(tmpDeploymentYaml, { context }); if (onUpdate) onUpdate(`Applied new deployment YAML of "${appSlug}" successfully.`); } catch (e) { const error = `[ERROR] Unable to apply new deployment "${deploymentName}": ${e}\n\n${deploymentYaml}`; if (onUpdate) onUpdate(error); // dispatch/trigger webhook if (webhook) webhookSvc.trigger(mongodb_1.MongoDB.toString(webhook._id), "failed"); // update release as "failed" await DB.update("release", { _id: releaseId }, { status: "failed" }, { select: ["_id", "status"] }).catch(console.error); // Update "deployStatus" of a build to success await DB.update("build", { _id: buildId }, { deployStatus: "failed" }, { select: ["_id", "deployStatus"] }).catch(console.error); return { error }; } /** * Annotate new deployment with app version */ try { await k8s_1.default.kubectlAnnotateDeployment(`kubernetes.io/change-cause="${message || appVersion}"`, deploymentName, namespace, { context, }); } catch (e) { const error = `Unable to annotate new deployment (Cluster: ${clusterSlug} / Namespace: ${namespace} / App: ${appSlug} / Env: ${env} / Deployment: ${deploymentName})`; if (onUpdate) onUpdate(error); // dispatch/trigger webhook if (webhook) webhookSvc.trigger(mongodb_1.MongoDB.toString(webhook._id), "failed"); // update release as "failed" await DB.update("release", { _id: releaseId }, { status: "failed" }, { select: ["_id", "status"] }).catch(console.error); // Update "deployStatus" of a build to success await DB.update("build", { _id: buildId }, { deployStatus: "failed" }, { select: ["_id", "deployStatus"] }).catch(console.error); return { error }; } /** * Wait until the deployment is ready! * (Ignore crashed pods) */ const isNewDeploymentReady = await (0, plugins_1.waitUntil)(async () => { const isReady = checkDeploymentReady({ context, appName: mainAppName, appVersion, namespace, onUpdate, skipCrashedPods: true, }); if (!isReady) { // Check if all new pods are crashed const newPods = await k8s_1.default.getPods(namespace, { context, filterLabel: `app-version=${appVersion}`, metrics: false }); const countNewCrashedPods = newPods.filter((pod) => pod.status.containerStatuses.find((containerStatus) => { var _a; return ((_a = containerStatus.state.waiting) === null || _a === void 0 ? void 0 : _a.reason) === "CrashLoopBackOff"; }) !== undefined).length; const isAllNewPodsCrashed = countNewCrashedPods === newPods.length; if (isAllNewPodsCrashed) { const error = `All new pods are crashed.`; if (onUpdate) onUpdate(error); // Dispatch/trigger webhook if (webhook) webhookSvc.trigger(mongodb_1.MongoDB.toString(webhook._id), "failed"); // Update release as "failed" await DB.update("release", { _id: releaseId }, { status: "failed" }, { select: ["_id", "status"] }).catch(console.error); // Update "deployStatus" of a build to "failed" await DB.update("build", { _id: buildId }, { deployStatus: "failed" }, { select: ["_id", "deployStatus"] }).catch(console.error); throw new Error(error); } } return isReady; }, // check interval: 5 secs 5, // max wait time: 10 mins 10 * 60).catch((e) => { (0, log_1.logError)(`[ROLL OUT V2] Error:`, e); return false; }); if (options === null || options === void 0 ? void 0 : options.isDebugging) (0, log_1.log)(`[ROLL OUT V2] Checking new deployment's status -> Is Fully Ready:`, isNewDeploymentReady); // Try to get the container logs and print to the web ui let containerLogs = isNewDeploymentReady ? await (0, kubectl_1.logPodByFilter)(namespace, { filterLabel: `app-version=${appVersion}`, context }).catch((e) => "") : await (0, kubectl_1.logPodByFilter)(namespace, { filterLabel: `app-version=${appVersion}`, previous: true, context }).catch((e) => ""); if (onUpdate && containerLogs) onUpdate(`--------------- APP'S LOGS ON STARTED UP --------------- \n${containerLogs}`); // throw the error if (!isNewDeploymentReady || containerLogs.indexOf("Error from server") > -1 || containerLogs.indexOf("An error occurred") > -1 || containerLogs.indexOf("Command failed") > -1 || containerLogs.indexOf("Unexpected Server Error") > -1) { const error = `[ERROR] The application failed to start up properly. To identify the issue, please review the application logs.`; if (onUpdate) onUpdate(error); // print out the logs in server side: (0, log_1.logError)(containerLogs); // dispatch/trigger webhook if (webhook) webhookSvc.trigger(mongodb_1.MongoDB.toString(webhook._id), "failed"); // update release as "failed" await DB.update("release", { _id: releaseId }, { status: "failed" }, { select: ["_id", "status"] }).catch(console.error); // Update "deployStatus" of a build to success await DB.update("build", { _id: buildId }, { deployStatus: "failed" }, { select: ["_id", "deployStatus"] }).catch(console.error); return { error }; } // Scale new deployment to new replicas await k8s_1.default.scaleDeploy(deploymentName, newReplicas, namespace, { context }); // Print success: const prodUrlInCLI = chalk_1.default.bold(`https://${endpointUrl}`); const successMsg = `🎉 PUBLISHED AT: ${prodUrlInCLI} 🎉`; (0, log_1.logSuccess)(successMsg); if (onUpdate) onUpdate(successMsg); // Mark this latest release as "active": try { const latestRelease = await (0, mark_release_as_active_1.markReleaseAsActive)({ id: releaseId, appSlug, env }); if (!latestRelease) throw new Error(`Release "${releaseId}" not found.`); } catch (e) { const error = `[ERROR] Unable to mark the latest release (${releaseId}) status as "active": ${e.message}`; if (onUpdate) onUpdate(error); // dispatch/trigger webhook if (webhook) webhookSvc.trigger(mongodb_1.MongoDB.toString(webhook._id), "failed"); // Update "deployStatus" of a build to "failed" await DB.update("build", { _id: buildId }, { deployStatus: "failed" }, { select: ["_id", "deployStatus"] }); throw new Error(error); } // Update "deployStatus" of a build to success const build = await DB.updateOne("build", { _id: buildId }, { deployStatus: "success" }, { select: ["_id", "deployStatus"] }); // Update project to sort by latest release await DB.update("project", { slug: projectSlug }, { lastUpdatedBy: owner.username, latestBuild: build === null || build === void 0 ? void 0 : build._id }, { select: ["_id", "updatedAt"] }); // Assign this release as "latestRelease" of this app's deploy environment await DB.updateOne("app", { slug: appSlug }, { [`deployEnvironment.${env}.latestRelease`]: releaseId, [`deployEnvironment.${env}.appVersion`]: appVersion, [`deployEnvironment.${env}.buildId`]: buildId, }, { select: ["_id"] }); /** * 5. Clean up > Delete old deployments (IF ANY) * - Skip CLEAN UP task on test environment */ if (!(0, app_config_1.IsTest)()) { /** * NOTE: Clean up DEPRECATED deployments (from OLD CLI <3.33.11 deployments) */ // if (isServerMode && env === "prod") { if (app_config_1.isServerMode) { cleanUpNamespace(cluster, namespace, mainAppName, appVersion) .then(({ error }) => { if (error) throw new Error(`Unable to clean up old resources in "${namespace}" namespace.`); (0, log_1.logSuccess)(`✅ Clean up old resources in "${namespace}" namespace SUCCESSFULLY.`); }) .catch((e) => (0, log_1.logError)(`Unable to clean up old resources in "${namespace}" namespace:`, e)); } } return { error: null, data: releaseData }; } exports.rolloutV2 = rolloutV2;