@topgroup/diginext
Version:
A BUILD SERVER & CLI to deploy apps to any Kubernetes clusters.
1,092 lines (1,091 loc) • 47 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.resolveDockerfilePath = exports.resolveFilePath = exports.sequentialExec = exports.objToEnv = exports.getDiginextEnvVars = exports.strToArray = exports.objectToDeploymentYaml = exports.kubeEnvToDotenv = exports.getValueOfKubeEnvVarsByName = exports.loadEnvFileAsContainerEnvVars = exports.objectToDotenv = exports.objectToKubeEnvVars = exports.trimFirstSlash = exports.getReplicasFromYaml = exports.getImageFromYaml = exports.getCurrentFramework = exports.isUsingStaticHtmlFramework = exports.isUsingDiginestAPIFramework = exports.isUsingDiginextFramework = exports.isUsingNodejsFramework = exports.isUsingExpressjsFramework = exports.getGitProviderFromRepoSSH = exports.getCurrentGitRepoData = exports.pullOrCloneGitRepo = exports.cloneGitRepo = exports.installPackages = exports.savePackageConfig = exports.getPackageConfig = exports.updateAppConfig = exports.saveAppConfig = exports.getAppConfig = exports.flattenObjectPaths = exports.flattenObjectToPost = exports.deleteFolderRecursive = exports.parseRepoSlugFromUrl = exports.shouldNotifyCliUpdate = exports.getLatestCliVersion = exports.currentVersion = exports.execCmd = exports.progressCmd = exports.logVersion = exports.replaceInFile = exports.getLongEnv = exports.stringToArray = exports.createTmpFile = exports.showDocs = exports.saveJson = exports.readJson = exports.waitUntil = exports.nowStr = void 0;
exports.generateWorkspaceApiAccessToken = exports.getUnexpiredAccessToken = exports.extractWorkspaceIdFromUser = exports.extractWorkspaceSlugFromUrl = exports.wait = exports.toBase64 = exports.logHelp = exports.logBitbucketError = exports.logBitbucket = exports.getCurrentContainerEnvs = exports.getCurrentImageName = exports.getCurrentDeployment = exports.getIngressIP = exports.getIngressEndpoint = exports.getIngress = exports.getClusterIP = exports.getIPFromDomain = exports.cliContainerExec = exports.resolveEnvFilePath = void 0;
// import { execa } from "execa";
const fs = __importStar(require("node:fs"));
const chalk_1 = __importDefault(require("chalk"));
const crypto_1 = require("crypto");
// import { compareVersions } from "compare-versions";
const dayjs_1 = __importDefault(require("dayjs"));
const makeDaySlug_1 = require("diginext-utils/dist/string/makeDaySlug");
const log_1 = require("diginext-utils/dist/xconsole/log");
const dns_1 = __importDefault(require("dns"));
const dotenv_1 = __importDefault(require("dotenv"));
const execa_1 = require("execa");
const afs = __importStar(require("fs/promises"));
const js_yaml_1 = __importDefault(require("js-yaml"));
const lodash_1 = __importStar(require("lodash"));
const m = __importStar(require("marked"));
const marked_terminal_1 = __importDefault(require("marked-terminal"));
const path_1 = __importDefault(require("path"));
const simple_git_1 = require("simple-git");
const package_json_1 = __importDefault(require("../../package.json"));
const config_1 = require("../config/config");
const git_utils_1 = require("../modules/git/git-utils");
const const_1 = require("../config/const");
const mongodb_1 = require("./mongodb");
const monorepo_1 = require("./monorepo");
const number_1 = require("./number");
const os_1 = require("./os");
const { marked } = m;
marked.setOptions({ renderer: new marked_terminal_1.default() });
function nowStr() {
return (0, dayjs_1.default)().format("YYYY-MM-DD HH:mm:ss");
}
exports.nowStr = nowStr;
/**
* Delay/wait a specific miliseconds
* @param i - waiting time in miliseconds
* @param exec - callback function
*/
const wait = async function (i = 100, exec) {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
if (exec)
exec();
resolve(true);
}
catch (e) {
reject(e);
}
}, i);
});
};
exports.wait = wait;
/**
* Wait until a condition is matched
* @param condition - Condition
* @param interval - Re-check interval in seconds @default 10
* @param maxWaitingTime - Max waiting time in seconds @default 30 minutes (30 * 60 = 1.800 seconds)
*/
async function waitUntil(condition, interval = 10, maxWaitTime = 30 * 60 * 1000) {
let timeWaited = 0;
while (timeWaited < maxWaitTime) {
const conditionMet = await condition();
if (conditionMet) {
return true;
}
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
timeWaited += interval;
}
return false;
}
exports.waitUntil = waitUntil;
async function logBitbucket(title, message, delay) {
if (delay)
await wait(delay);
console.log(chalk_1.default.yellow("====== [BITBUCKET: " + title + "] ======"));
console.log(message);
process.exit(1);
}
exports.logBitbucket = logBitbucket;
const readJson = (filePath) => {
const jsonContent = fs.readFileSync(filePath).toString();
return JSON.parse(jsonContent);
};
exports.readJson = readJson;
const saveJson = (data, filePath, options = {}) => {
const { overwrite = false, beautify = true } = options;
let jsonContent = typeof data == "string" ? data : JSON.stringify(data);
if (beautify)
jsonContent = JSON.stringify(JSON.parse(jsonContent), null, 4);
const fileExisted = fs.existsSync(filePath);
if (fileExisted && !overwrite) {
try {
return JSON.parse(fs.readFileSync(filePath).toString());
}
catch (e) {
(0, log_1.logWarn)(`FILE_EXISTED >`, e);
return {};
}
}
fs.writeFileSync(filePath, jsonContent, "utf8");
return JSON.parse(jsonContent);
};
exports.saveJson = saveJson;
const showDocs = async (filePath) => {
// const cliMd = await importEsm("cli-markdown", module);
// console.log("cliMd :>> ", cliMd);
const content = await afs.readFile(filePath, "utf8");
(0, log_1.log)(marked(content));
// log(cliMd(content));
return content;
};
exports.showDocs = showDocs;
/**
* Create temporary file with provided content
* @param fileName - File name (include the extension)
* @param content - Content of the file
* @returns Path to the file
*/
const createTmpFile = (fileName, content, options = { recursive: true, encoding: "utf8" }) => {
const { encoding, recursive } = options;
const tmpDir = path_1.default.resolve(const_1.HOME_DIR, `tmp/${(0, makeDaySlug_1.makeDaySlug)({ divider: "" })}`);
if (!fs.existsSync(tmpDir))
fs.mkdirSync(tmpDir, { recursive });
const tmpFilePath = path_1.default.resolve(tmpDir, fileName);
fs.writeFileSync(tmpFilePath, content, encoding);
return tmpFilePath;
};
exports.createTmpFile = createTmpFile;
/**
* Convert string-array-like to array
* @example "1" -> ["1"] | "123,555,abc,def" -> ["123","555","abc","def"]
*/
const stringToArray = (str, options = { typeTransform: false, divider: "," }) => {
const { typeTransform = false, divider = "," } = options;
const arr = str.indexOf(divider) === -1 ? [str] : str.split(divider);
return typeTransform ? arr.map((item) => ((0, number_1.isNumeric)(item) ? (0, lodash_1.toNumber)(item) : item)) : arr;
};
exports.stringToArray = stringToArray;
/**
* Get full name of the environment, such as: `development`, `production` (instead of `dev`, `prod`)
* @param {String} env
* @returns {String}
*/
const getLongEnv = (env) => {
if (env == "dev") {
return "development";
}
else if (env == "prod") {
return "production";
}
else if (env == "cana") {
return "canary";
}
else if (env == "stag") {
return "staging";
}
else {
return env;
}
};
exports.getLongEnv = getLongEnv;
/**
* @param {String} filePath
* @param {[{keyword:(RegExp|String), replacement:String}]} replacement=[]
* @return {String} - New content
*/
const replaceInFile = async (filePath, replacement = []) => {
let fileContent = fs.readFileSync(filePath, "utf-8");
replacement.forEach(({ keyword, replacement: replaceWith }) => {
fileContent = fileContent.replace(keyword, replaceWith);
});
fs.writeFileSync(filePath, fileContent, "utf8");
return fileContent;
};
exports.replaceInFile = replaceInFile;
function toBase64(str) {
return Buffer.from(str).toString("base64");
}
exports.toBase64 = toBase64;
function logVersion() {
console.warn(chalk_1.default.bgWhite(chalk_1.default.bold(chalk_1.default.black(` [ Diginext CLI - v${package_json_1.default.version} | ${nowStr()} ] `))));
}
exports.logVersion = logVersion;
const progressCmd = async (command, options) => {
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("Processing command :>> ", command);
const stream = (0, execa_1.execaCommand)(command);
let stdout = "";
stream.stdio.forEach((_stdio) => {
if (_stdio) {
_stdio.on("data", (data) => {
let logMsg = data.toString();
stdout += logMsg;
if ((options === null || options === void 0 ? void 0 : options.isDebugging) && logMsg)
console.log(logMsg);
if ((options === null || options === void 0 ? void 0 : options.onProgress) && logMsg)
options === null || options === void 0 ? void 0 : options.onProgress(logMsg);
});
}
});
const end = await stream;
return stdout || end.stdout;
};
exports.progressCmd = progressCmd;
async function execCmd(cmd, errorMsgOrCallback = "") {
try {
let { stdout } = await (0, execa_1.execaCommand)(cmd, config_1.cliOpts);
// console.log(`[execCmd]`, { stdout });
return stdout;
}
catch (e) {
if (typeof errorMsgOrCallback == "string") {
const errorMsg = errorMsgOrCallback;
if (errorMsg != "") {
(0, log_1.logError)(`${errorMsg} > ${e.stack}`);
}
else {
(0, log_1.logWarn)(`[FAILED_BUT_IGNORE] ${e.message}`);
}
return;
}
else {
// if it's a callback function
try {
errorMsgOrCallback(e);
}
catch (f) {
(0, log_1.logWarn)(`[FAILED_BUT_IGNORE] ${f.message}`);
return;
}
}
}
}
exports.execCmd = execCmd;
/**
* Get current CLI version
*/
function currentVersion() {
return package_json_1.default.version;
}
exports.currentVersion = currentVersion;
/**
* Get latest version of the CLI from NPM
*/
async function getLatestCliVersion() {
const latestVersion = await execCmd(`npm show ${package_json_1.default.name} version`);
return latestVersion;
}
exports.getLatestCliVersion = getLatestCliVersion;
/**
* Check if CLI version is latest or not, if not -> return FALSE
*/
async function shouldNotifyCliUpdate() {
const curVersion = currentVersion();
if (curVersion.indexOf("prerelease") > -1)
return false;
const latestVersion = await getLatestCliVersion();
let [lastestBreaking, lastestFeature, lastestPatch] = latestVersion.split(".").map((num) => (0, lodash_1.toInteger)(num));
let [curBreaking, curFeature, curPatch] = curVersion.split(".").map((num) => (0, lodash_1.toInteger)(num));
if (lastestBreaking > curBreaking)
return false; // no need to notify when there is a new breaking change version
if (lastestBreaking === curBreaking && lastestFeature > curFeature)
return true;
if (lastestBreaking === curBreaking && lastestFeature === curFeature && lastestPatch > curPatch)
return true;
return false;
}
exports.shouldNotifyCliUpdate = shouldNotifyCliUpdate;
async function logBitbucketError(error, delay, location, shouldExit = false) {
if (delay)
await wait(delay);
try {
delete error.request.request;
}
catch (e) { }
console.error(error);
const errMsg = error.message ? error.message : error.error.message;
const reason = error.error && error.error.error && error.error.error.fields ? error.error.error.fields : error.error.error.message;
console.error(chalk_1.default.red(`[ERROR ${error.status}: ${errMsg} | ${nowStr()}]`));
console.error(chalk_1.default.yellow("[REASON]"), reason);
if (location)
console.error(chalk_1.default.yellow("[LOCATION]"), location);
console.error(error.request);
if (shouldExit)
process.exit(1);
}
exports.logBitbucketError = logBitbucketError;
const parseRepoSlugFromUrl = (url) => {
// https://digitop-duynguyen@bitbucket.org/digitopvn/diginext-cli.git
let n = url.split("/").pop().split(".").shift();
return n;
};
exports.parseRepoSlugFromUrl = parseRepoSlugFromUrl;
const deleteFolderRecursive = async (dir) => {
if (fs.existsSync(dir)) {
// for (let entry of await afs.readdir(dir)) {
// const filePath = path.resolve(dir, entry);
// // use "unlink" to delete every single file
// if ((await afs.lstat(filePath)).isDirectory()) await deleteFolderRecursive(filePath);
// else await afs.unlink(filePath);
// }
// remove the directory itself
await afs.rm(dir, { recursive: true, force: true });
}
};
exports.deleteFolderRecursive = deleteFolderRecursive;
/**
* Flatten the object into 1-level-object (with key paths)
* @example {a: {b: [{c: 1}, {c: 2}]}, e: 3} -> {"a.b[0].c": 1, "a.b[1].c": 2, "e": 3}
*/
function flattenObjectToPost(object = {}, initialPathPrefix = "") {
if (!object || typeof object !== "object")
return [{ [initialPathPrefix]: object }];
const prefix = initialPathPrefix ? (Array.isArray(object) ? initialPathPrefix : `${initialPathPrefix}`) : "";
const _arr = Object.entries(object).flatMap(([key]) => flattenObjectToPost(object[key], Array.isArray(object) ? `${prefix}[${key}]` : `${prefix}[${key}]`));
// console.log("_arr :>> ", _arr);
if ((0, lodash_1.isEmpty)(_arr))
return {};
const res = _arr.reduce((acc, _path) => ({ ...acc, ..._path }));
// console.log("res :>> ", res);
return res;
}
exports.flattenObjectToPost = flattenObjectToPost;
/**
* Flatten the object into 1-level-object (with key paths)
* @example {a: {b: [{c: 1}, {c: 2}]}, e: 3} -> {"a.b.0.c": 1, "a.b.1.c": 2, "e": 3}
*/
function flattenObjectPaths(object = {}, initialPathPrefix = "") {
if (!object || typeof object !== "object")
return [{ [initialPathPrefix]: object }];
const prefix = initialPathPrefix ? (Array.isArray(object) ? initialPathPrefix : `${initialPathPrefix}.`) : "";
const _arr = Object.entries(object).flatMap(([key]) => flattenObjectPaths(object[key], Array.isArray(object) ? `${prefix}.${key}` : `${prefix}${key}`));
// console.log("_arr :>> ", _arr);
if ((0, lodash_1.isEmpty)(_arr))
return {};
const res = _arr.reduce((acc, _path) => ({ ...acc, ..._path }));
// console.log("res :>> ", res);
return res;
}
exports.flattenObjectPaths = flattenObjectPaths;
/**
* Get object of project configuration from "dx.json"
* @param {String} [directory] - Absolute path to project directory
*/
const getAppConfig = (directory) => {
const filePath = path_1.default.resolve(directory || process.cwd(), "dx.json");
if (!fs.existsSync(filePath))
return;
const cfg = (0, exports.readJson)(filePath);
return cfg;
};
exports.getAppConfig = getAppConfig;
/**
* Save object of project configuration to "dx.json"
* @param {Object} appConfig - Object data of the config
* @param {SaveOpts} [options] - Save options
* @param {String} [options.directory] - Absolute path to project directory @default process.cwd()
* @param {Boolean} [options.create] - TRUE will create new file if not existed. @default false
*/
const saveAppConfig = (appConfig, options = { directory: process.cwd(), create: false }) => {
const { directory, create } = options;
const filePath = path_1.default.resolve(directory || process.cwd(), "dx.json");
if (!create && !fs.existsSync(filePath))
(0, log_1.logError)(`Không tìm thấy "dx.json"`);
if (fs.existsSync(filePath))
fs.unlinkSync(filePath);
const content = JSON.stringify(appConfig, null, 2);
fs.writeFileSync(filePath, content, "utf8");
return (0, exports.getAppConfig)(directory);
};
exports.saveAppConfig = saveAppConfig;
/**
* Update values of app config ("dx.json")
* @param updatedData - updated data
*/
const updateAppConfig = (updatedData, options = {}) => {
const { directory = process.cwd() } = options;
const currentAppConfig = (0, exports.getAppConfig)(directory);
const updatedDataMap = flattenObjectPaths(updatedData);
Object.entries(updatedDataMap).map(([keyPath, value]) => {
lodash_1.default.set(currentAppConfig, keyPath, value);
});
(0, exports.saveAppConfig)(currentAppConfig, { directory });
return currentAppConfig;
};
exports.updateAppConfig = updateAppConfig;
/**
* Get object of project configuration from "../../package.json"
* @param {Object} [options] - Options
* @param {String} [options.directory] - Absolute path to project directory
* @param {Boolean} [options.ignoreIfNotExisted] - TRUE ignore the error if not existed.
* @return {Object}
*/
const getPackageConfig = (options) => {
const { directory, ignoreIfNotExisted = true } = options;
let shouldReturn = true;
const filePath = path_1.default.resolve(directory || process.cwd(), "package.json");
if (!fs.existsSync(filePath)) {
if (ignoreIfNotExisted) {
shouldReturn = false;
}
else {
(0, log_1.logError)(`Không tìm thấy "package.json"`);
}
}
return shouldReturn ? (0, exports.readJson)(filePath) : {};
};
exports.getPackageConfig = getPackageConfig;
/**
* Save object of project configuration to "package.json"
* @param {Object} _config - Object data of the config
* @param {SaveOpts} [options] - Options
* @param {String} [options.directory] - Absolute path to project directory
* @param {Boolean} [options.create] - TRUE will create new file if not existed.
* @param {Boolean} [options.ignoreIfNotExisted] - TRUE ignore the error if not existed.
*/
const savePackageConfig = (_config, options) => {
const { directory, ignoreIfNotExisted } = options;
let shouldWriteFile = true;
const filePath = path_1.default.resolve(directory || process.cwd(), "package.json");
if (!options.create && !fs.existsSync(filePath) && !ignoreIfNotExisted) {
(0, log_1.logError)(`Không tìm thấy "package.json"`);
shouldWriteFile = false;
}
if (shouldWriteFile) {
const content = JSON.stringify(_config, null, 2);
return fs.writeFileSync(filePath, content, "utf8");
}
};
exports.savePackageConfig = savePackageConfig;
/**
* Process `npm install` or `yarn install` or `pnpm install` on current directory
*/
const installPackages = async () => {
(0, log_1.log)(`Đang tiến hành cài đặt "package.json" mới...`);
let areDependenciesInstalled = false;
// Install dependencies
try {
await (0, execa_1.execa)("yarn", ["install"]);
// console.log(stdout);
areDependenciesInstalled = true;
}
catch (e) {
(0, log_1.logWarn)("YARN not found, switch to `npm install` instead.");
}
if (!areDependenciesInstalled) {
let isOk;
try {
await (0, execa_1.execa)("npm", ["install"]);
isOk = true;
}
catch (e) {
(0, log_1.logError)("NPM not found -> ", e);
isOk = false;
}
return isOk;
}
else {
return true;
}
};
exports.installPackages = installPackages;
const cloneGitRepo = async (repoSSH, dir, options = {}) => {
//
let git;
const { onUpdate } = options;
const onProgress = ({ method, stage, progress }) => {
const message = `git.${method} ${stage} stage ${progress}% complete`;
if (onUpdate)
onUpdate(message, progress);
};
if (fs.existsSync(dir)) {
try {
git = (0, simple_git_1.simpleGit)(dir, { progress: onProgress });
await git.clone(repoSSH, dir, ["--depth", "1"]);
console.log("\ndone");
}
catch (e) { }
}
};
exports.cloneGitRepo = cloneGitRepo;
//
const pullOrCloneGitRepo = async (repoSSH, dir, branch, options = {}) => {
var _a;
let git;
let success = false;
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("pullOrCloneGitRepo() > repoSSH :>> ", repoSSH);
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("pullOrCloneGitRepo() > dir :>> ", dir);
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("pullOrCloneGitRepo() > options :>> ", options);
const onProgress = ({ method, stage, progress }) => {
const message = `git.${method} ${stage} stage ${progress}% complete`;
if (options === null || options === void 0 ? void 0 : options.onUpdate)
options === null || options === void 0 ? void 0 : options.onUpdate(message, progress);
};
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("pullOrCloneGitRepo() > repoSSH :>> ", repoSSH);
// TMP DIRECTORY NOT EXISTS
if (!fs.existsSync(dir)) {
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("pullOrCloneGitRepo() > directory NOT exists :>> try to CLONE...");
if (options === null || options === void 0 ? void 0 : options.onUpdate)
options === null || options === void 0 ? void 0 : options.onUpdate(`Cache source code not found. Cloning "${repoSSH}" (${branch}) to "${dir}" directory.`);
try {
git = (0, simple_git_1.simpleGit)({ progress: onProgress });
await git.clone(repoSSH, dir, [`--branch=${branch}`, "--single-branch", "--depth=1"]);
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("✅ pullOrCloneGitRepo() > Success to CLONE !");
// remove git on finish
if (options === null || options === void 0 ? void 0 : options.removeGitOnFinish)
await (0, exports.deleteFolderRecursive)(path_1.default.join(dir, ".git"));
if (options === null || options === void 0 ? void 0 : options.removeCIOnFinish)
await (0, exports.deleteFolderRecursive)(path_1.default.join(dir, ".github"));
success = true;
return success;
}
catch (e) {
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("pullOrCloneGitRepo() > Failed to CLONE !");
if (options === null || options === void 0 ? void 0 : options.onUpdate)
options === null || options === void 0 ? void 0 : options.onUpdate(`Failed to clone "${repoSSH}" (${branch}) to "${dir}" directory: ${e.message}`);
}
}
// TMP DIRECTORY -> EXISTS
try {
// try pull
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("pullOrCloneGitRepo() > directory exists :>> try to PULL...");
git = (0, simple_git_1.simpleGit)(dir, { progress: onProgress });
// -----------------------
// ! DO NOT SET TO "FALSE"
// -----------------------
const remotes = ((await git.getRemotes(true)) || []).filter((remote) => remote.name === "origin");
const originRemote = remotes[0];
if (!originRemote)
throw new Error(`This directory doesn't have any git remotes.`);
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("originRemote :>> ", originRemote, `>`, { repoSSH });
if (((_a = originRemote === null || originRemote === void 0 ? void 0 : originRemote.refs) === null || _a === void 0 ? void 0 : _a.fetch) !== repoSSH)
await git.addRemote("origin", repoSSH);
const curBranch = await (0, git_utils_1.getCurrentGitBranch)(dir);
await git.pull("origin", curBranch, ["--no-ff"]);
// remove git on finish
if (options === null || options === void 0 ? void 0 : options.removeGitOnFinish)
await (0, exports.deleteFolderRecursive)(path_1.default.join(dir, ".git"));
if (options === null || options === void 0 ? void 0 : options.removeCIOnFinish)
await (0, exports.deleteFolderRecursive)(path_1.default.join(dir, ".github"));
success = true;
}
catch (e) {
// if PULL failed -> delete dir -> try CLONE
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("pullOrCloneGitRepo() > Failed to PULL :>> try to CLONE...", e);
if (options === null || options === void 0 ? void 0 : options.onUpdate)
options === null || options === void 0 ? void 0 : options.onUpdate(`Failed to pull "${repoSSH}" in "${dir}" directory (${e.message}) -> trying to clone new...`);
try {
// just for sure...
await (0, exports.deleteFolderRecursive)(dir);
// for CLI create new app from a framework
git = (0, simple_git_1.simpleGit)({ progress: onProgress });
await git.clone(repoSSH, dir, [`--branch=${branch}`, "--single-branch", "--depth=1"]);
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("pullOrCloneGitRepo() > Success to CLONE !");
// remove git on finish
if (options === null || options === void 0 ? void 0 : options.removeGitOnFinish)
await (0, exports.deleteFolderRecursive)(path_1.default.join(dir, ".git"));
if (options === null || options === void 0 ? void 0 : options.removeCIOnFinish)
await (0, exports.deleteFolderRecursive)(path_1.default.join(dir, ".github"));
success = true;
}
catch (e2) {
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("pullOrCloneGitRepo() > Failed to PULL & CLONE :>> ", e2);
if (options === null || options === void 0 ? void 0 : options.onUpdate)
options === null || options === void 0 ? void 0 : options.onUpdate(`Failed to clone "${repoSSH}" (${branch}) to "${dir}" directory: ${e2.message}`);
}
}
return success;
};
exports.pullOrCloneGitRepo = pullOrCloneGitRepo;
/**
* Get current remote SSH & URL
*/
const getCurrentGitRepoData = async (dir = process.cwd(), options) => {
var _a, _b;
try {
const git = (0, simple_git_1.simpleGit)(dir, {
baseDir: `${dir}`,
binary: "git",
});
// -----------------------
// ! DO NOT SET TO "FALSE"
// -----------------------
const remotes = await git.getRemotes(true);
if (options === null || options === void 0 ? void 0 : options.isDebugging) {
console.log("[CURRENT DIR] Current git > remotes :>>");
console.dir(remotes, { depth: 10 });
}
const repoSshOrUrl = (_b = (_a = remotes[0]) === null || _a === void 0 ? void 0 : _a.refs) === null || _b === void 0 ? void 0 : _b.fetch;
if (!repoSshOrUrl)
return;
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("getCurrentGitRepoData() > repoSshOrUrl :>> ", repoSshOrUrl);
const branch = await (0, git_utils_1.getCurrentGitBranch)(dir);
if (!branch)
return;
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("getCurrentGitRepoData() > branch :>> ", branch);
const repoSSH = (0, git_utils_1.isValidRepoURL)(repoSshOrUrl) ? (0, git_utils_1.repoUrlToRepoSSH)(repoSshOrUrl) : repoSshOrUrl;
const repoURL = (0, git_utils_1.isValidRepoURL)(repoSshOrUrl) ? repoSshOrUrl : (0, git_utils_1.repoSshToRepoURL)(repoSshOrUrl);
if (options === null || options === void 0 ? void 0 : options.isDebugging) {
console.log("getCurrentGitRepoData() > repoSSH :>> ", repoSSH);
console.log("getCurrentGitRepoData() > repoURL :>> ", repoURL);
}
const gitData = (0, git_utils_1.parseGitRepoDataFromRepoSSH)(repoSSH);
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.log("getCurrentGitRepoData() > gitData :>> ", gitData);
const { repoSlug: slug, providerType: provider, namespace, gitDomain, fullSlug } = gitData;
return { repoSSH, repoURL, provider, slug, fullSlug, namespace, gitDomain, branch };
}
catch (e) {
if (options === null || options === void 0 ? void 0 : options.isDebugging)
console.warn(`getCurrentGitRepoData() > error :>>`, e.toString());
return;
}
};
exports.getCurrentGitRepoData = getCurrentGitRepoData;
const getGitProviderFromRepoSSH = (repoSSH) => {
if (repoSSH.indexOf("bitbucket") > -1)
return "bitbucket";
if (repoSSH.indexOf("github") > -1)
return "github";
// if (repoSSH.indexOf("gitlab") > -1) return "gitlab";
return;
};
exports.getGitProviderFromRepoSSH = getGitProviderFromRepoSSH;
const isUsingExpressjsFramework = (options) => {
let val = false;
const { error, appDirectory } = (0, monorepo_1.checkMonorepo)(options);
if (error)
return val;
// framework name
const frameworkConfigPath = path_1.default.resolve(appDirectory || process.cwd(), "package.json");
// TODO: check if using express js
return val;
};
exports.isUsingExpressjsFramework = isUsingExpressjsFramework;
const isUsingNodejsFramework = (options) => {
let val = false;
const { error, appDirectory } = (0, monorepo_1.checkMonorepo)(options);
if (error)
return val;
// framework name
const frameworkConfigPath = path_1.default.resolve(appDirectory || process.cwd(), "package.json");
return val;
};
exports.isUsingNodejsFramework = isUsingNodejsFramework;
const isUsingDiginextFramework = async (options) => {
var _a;
let val = false;
const { error, appDirectory } = (0, monorepo_1.checkMonorepo)(options);
if (error)
return val;
// framework name
const frameworkConfigPath = path_1.default.resolve(appDirectory || process.cwd(), "package.json");
if (fs.existsSync(frameworkConfigPath)) {
const frameworkConfig = await (_a = frameworkConfigPath, Promise.resolve().then(() => __importStar(require(_a))));
val = frameworkConfig.dependencies && frameworkConfig.dependencies.hasOwnProperty("next");
}
return val;
};
exports.isUsingDiginextFramework = isUsingDiginextFramework;
const isUsingDiginestAPIFramework = async (options) => {
var _a;
let val = false;
const { error, appDirectory } = (0, monorepo_1.checkMonorepo)(options);
if (error)
return val;
// framework name
const frameworkConfigPath = path_1.default.resolve(appDirectory || process.cwd(), "package.json");
if (fs.existsSync(frameworkConfigPath)) {
const frameworkConfig = await (_a = frameworkConfigPath, Promise.resolve().then(() => __importStar(require(_a))));
val = frameworkConfig.dependencies && frameworkConfig.dependencies.hasOwnProperty("@nestjs/core");
}
return val;
};
exports.isUsingDiginestAPIFramework = isUsingDiginestAPIFramework;
const isUsingStaticHtmlFramework = async (options) => {
var _a;
let val = false;
const { error, appDirectory } = (0, monorepo_1.checkMonorepo)(options);
if (error)
return val;
const frameworkConfigPath = path_1.default.resolve(appDirectory || process.cwd(), "dx.json");
if (fs.existsSync(frameworkConfigPath)) {
const frameworkConfig = await (_a = frameworkConfigPath, Promise.resolve().then(() => __importStar(require(_a))));
val = typeof frameworkConfig.framework != "undefined" && frameworkConfig.framework == "static";
}
return val;
};
exports.isUsingStaticHtmlFramework = isUsingStaticHtmlFramework;
/**
* Get current using framework of the project.
* @return {("unknown"|"diginest"|"diginext"|"nodejs"|"expressjs"|"static")}
*/
const getCurrentFramework = (options) => {
let val = "unknown";
if ((0, exports.isUsingDiginextFramework)(options))
val = "diginext";
if ((0, exports.isUsingDiginestAPIFramework)(options))
val = "diginest";
if ((0, exports.isUsingNodejsFramework)(options))
val = "nodejs";
if ((0, exports.isUsingExpressjsFramework)(options))
val = "expressjs";
if ((0, exports.isUsingStaticHtmlFramework)(options))
val = "static";
return val;
};
exports.getCurrentFramework = getCurrentFramework;
const getImageFromYaml = (docs) => {
let value = "";
docs.map((doc) => {
// log("doc", doc);
if (doc && doc.kind == "Deployment") {
value = doc.spec.template.spec.containers[0].image;
}
});
return value;
};
exports.getImageFromYaml = getImageFromYaml;
const getReplicasFromYaml = (docs) => {
let value = 1;
docs.map((doc) => {
// log("doc", doc);
if (doc && doc.kind == "Deployment") {
value = doc.spec.replicas;
}
});
return value;
};
exports.getReplicasFromYaml = getReplicasFromYaml;
/**
* Completely remove the first / of the string
* @param {String} input
* @returns {String}
*/
const trimFirstSlash = (input) => {
// trim first slash of BASE_PATH:
let output = input;
for (let i = 0; i < 10; i++) {
if (output.length > 0 && output.charAt(0) == "/")
output = output.substr(1);
}
return output;
};
exports.trimFirstSlash = trimFirstSlash;
/**
* Convert {Object} to environment variables of Kuberketes container
* @param {Object} object - Input raw object, **not containing any methods**
*/
const objectToKubeEnvVars = (object) => {
return Object.entries(object).map(([name, value]) => {
return { name, value };
});
};
exports.objectToKubeEnvVars = objectToKubeEnvVars;
/**
* Convert {Object} to .env content
* @param {Object} object - Input raw object, **not containing any methods**
* @returns {String}
*/
const objectToDotenv = (object) => {
let content = "";
for (let key in object) {
let val = object[key];
content += key + "=" + `"${val}"` + "\n";
}
return content;
};
exports.objectToDotenv = objectToDotenv;
/**
* Load ENV file (.env.*) and parse to array of K8S container environment variables
*/
const loadEnvFileAsContainerEnvVars = (filePath) => {
const envObject = dotenv_1.default.config({ path: filePath }).parsed;
if ((0, lodash_1.isEmpty)(envObject))
return [];
return (0, exports.objectToKubeEnvVars)(envObject);
};
exports.loadEnvFileAsContainerEnvVars = loadEnvFileAsContainerEnvVars;
/**
* Grab value of Kube ENV variables by name
*/
const getValueOfKubeEnvVarsByName = (name, envVars) => {
var _a;
return (_a = envVars.find((envVar) => envVar.name === name)) === null || _a === void 0 ? void 0 : _a.value;
};
exports.getValueOfKubeEnvVarsByName = getValueOfKubeEnvVarsByName;
/**
* Convert K8S container's ENV to .env content
* @param {[{name,value}]} inputEnvs - Input raw object, **not containing any methods**
* @returns {String}
*/
const kubeEnvToDotenv = (inputEnvs) => {
let content = "";
inputEnvs.map((envVar) => {
content += envVar.name + "=" + `"${envVar.value}"` + "\n";
});
return content;
};
exports.kubeEnvToDotenv = kubeEnvToDotenv;
const objectToDeploymentYaml = (deploymentCfg) => {
let deploymentContent = "";
if (!(0, lodash_1.isArray)(deploymentCfg))
deploymentCfg = [deploymentCfg];
deploymentCfg.map((doc) => {
if (doc) {
deploymentContent += js_yaml_1.default.dump(doc);
deploymentContent += "\n---\n";
}
});
return deploymentContent;
};
exports.objectToDeploymentYaml = objectToDeploymentYaml;
const strToArray = (str, splitter = ",") => {
let arr = [];
if (str.indexOf(splitter) > -1) {
arr = [...str.split(splitter)];
}
else {
arr = [str];
}
return arr;
};
exports.strToArray = strToArray;
const getDiginextEnvVars = (env, projectSlug, domains) => {
let environment = "staging";
if (env == "prod")
environment = "production";
return {
NEXT_PUBLIC_ENV: environment,
NEXT_PUBLIC_CDN_BASE_PATH: `${const_1.DIGITOP_CDN_URL}/${projectSlug}/${env}`,
NEXT_PUBLIC_BASE_PATH: typeof domains != "undefined" && domains.length ? "" : projectSlug,
NEXT_PUBLIC_BASE_URL: typeof domains != "undefined" && domains.length ? `https://${domains[0]}` : ``,
IRON_SESSION_SECRET: "NcvPDa2tlje1i6nvzZt6PmCHU5qcTcx4",
};
};
exports.getDiginextEnvVars = getDiginextEnvVars;
const objToEnv = (obj = {}) => {
let content = "";
for (const [key, val] of Object.entries(obj)) {
let value = val;
if ((0, lodash_1.isString)(val))
value = `"${val}"`;
content += `${key}=${value}\n`;
}
return content;
};
exports.objToEnv = objToEnv;
const sequentialExec = async (array, func) => {
if (typeof func == "undefined") {
(0, log_1.logWarn)("Input function not defined.");
return;
}
return array.reduce(async (previous, item) => {
await func(item);
return item;
}, Promise.resolve([]));
};
exports.sequentialExec = sequentialExec;
/**
* Resolve a location path of the file within the application.
*/
const resolveFilePath = (fileNamePrefix, options) => {
const { targetDirectory = process.cwd(), env = "dev", ignoreIfNotExisted = false, msg } = options;
let filePath = path_1.default.resolve(targetDirectory, `${fileNamePrefix}.${env}`);
if (fs.existsSync(filePath))
return filePath;
filePath = path_1.default.resolve(targetDirectory, `deployment/${fileNamePrefix}.${env}`);
if (fs.existsSync(filePath))
return filePath;
filePath = path_1.default.resolve(targetDirectory, fileNamePrefix);
if (fs.existsSync(filePath))
return filePath;
filePath = path_1.default.resolve(targetDirectory, `deployment/${fileNamePrefix}`);
if (fs.existsSync(filePath))
return filePath;
filePath = path_1.default.resolve(targetDirectory, fileNamePrefix);
if (fs.existsSync(filePath))
return filePath;
if (!ignoreIfNotExisted) {
const message = msg !== null && msg !== void 0 ? msg : `Missing "./${fileNamePrefix}" file, are you in the project directory?`;
throw new Error(message);
}
else {
if (msg)
(0, log_1.logWarn)(msg);
}
return;
};
exports.resolveFilePath = resolveFilePath;
/**
* Resolve a location path of the "Dockerfile".
*/
const resolveDockerfilePath = (options) => (0, exports.resolveFilePath)("Dockerfile", options);
exports.resolveDockerfilePath = resolveDockerfilePath;
/**
* Resolve a location path of the DOTENV (`.env.*`) file.
*/
const resolveEnvFilePath = (options) => (0, exports.resolveFilePath)(".env", options);
exports.resolveEnvFilePath = resolveEnvFilePath;
/**
* Execute an command within a Docker container
* @deprecated
*/
const cliContainerExec = async (command, options) => {
let getContainerName, cliContainerName;
// restart the CLI container to update the environment:
// if (!options.pipelineReady) {
// await startPipeline([], options);
// await wait(2000);
// options.pipelineReady = true;
// }
if ((0, os_1.isWin)()) {
getContainerName = await (0, execa_1.execaCommand)(`docker ps --format '{{.Names}}' | findstr diginext-cli`);
}
else {
getContainerName = await (0, execa_1.execaCommand)(`docker ps --format '{{.Names}}' | grep diginext-cli`);
}
cliContainerName = getContainerName.stdout;
(0, log_1.log)("[cliContainerExec] cliContainerName:", cliContainerName);
if (cliContainerName) {
if (options.isDebugging) {
(0, log_1.log)(chalk_1.default.cyan("---------------- DIGINEXT-CLI DOCKER VERSION ------------------"));
await (0, execa_1.execa)("docker", ["exec", "-ti", cliContainerName, "docker", "-v"], { stdio: "inherit" });
(0, log_1.log)(chalk_1.default.cyan("---------------- INSIDE DIGINEXT-CLI CONTAINER ----------------"));
await (0, execa_1.execa)("docker", ["exec", "-ti", cliContainerName, "ls"], { stdio: "inherit" });
(0, log_1.log)(chalk_1.default.cyan("---------------------------------------"));
}
const args = command.split(" ");
const { stdout } = await (0, execa_1.execa)("docker", ["exec", "-ti", cliContainerName, ...args], {
stdio: "inherit",
});
return stdout;
}
else {
return false;
}
};
exports.cliContainerExec = cliContainerExec;
async function logHelp(options) {
(0, log_1.log)(`Nothing... bwahahaha...`);
}
exports.logHelp = logHelp;
const getIPFromDomain = async (domain) => {
return new Promise((resolve, reject) => {
dns_1.default.lookup(domain, (err, address, family) => {
if (err) {
(0, log_1.logError)(`${domain} chưa được trỏ về IP nào.`);
reject(err);
}
resolve(address);
});
});
};
exports.getIPFromDomain = getIPFromDomain;
const getClusterIP = async (options) => {
let svcName = "nginx-ingress-controller", namespace = "nginx-ingress";
let ingress, stdout;
try {
stdout = await (0, exports.cliContainerExec)(`kubectl get svc/${svcName} -n ${namespace} -o json`, options);
ingress = stdout ? JSON.parse(stdout) : null;
}
catch (e) {
svcName = "ingress-nginx-controller";
namespace = "ingress-nginx";
stdout = await (0, exports.cliContainerExec)(`kubectl get svc/${svcName} -n ${namespace} -o json`, options);
ingress = stdout ? JSON.parse(stdout) : null;
}
return ingress ? ingress.status.loadBalancer.ingress[0].ip : null;
};
exports.getClusterIP = getClusterIP;
const getIngress = async (ingName, namespace = "default", options = {}) => {
let stdout = await (0, exports.cliContainerExec)(`kubectl get ing/${ingName} -n ${namespace} -o json`, options);
const ingress = stdout ? JSON.parse(stdout) : null;
return ingress;
};
exports.getIngress = getIngress;
const getIngressEndpoint = async (ingName, namespace = "default", options = {}) => {
let ingress = await (0, exports.getIngress)(ingName, namespace, options);
return ingress.spec.rules[0].host;
};
exports.getIngressEndpoint = getIngressEndpoint;
const getIngressIP = async (ingName, namespace = "default", index = 0, options = {}) => {
let stdout = await (0, exports.cliContainerExec)(`kubectl get ing/${ingName} -n ${namespace} -o json`, options);
const ingress = stdout ? JSON.parse(stdout) : null;
return ingress ? ingress.status.loadBalancer.ingress[index].ip : null;
};
exports.getIngressIP = getIngressIP;
const getCurrentDeployment = async (deployName, namespace = "default", options = {}) => {
let stdout = await (0, exports.cliContainerExec)(`kubectl get deploy/${deployName} -n ${namespace} -o json`, options);
if (typeof stdout == "string") {
try {
return JSON.parse(stdout);
}
catch (e) {
return null;
}
}
else {
return null;
}
};
exports.getCurrentDeployment = getCurrentDeployment;
const getCurrentImageName = async (deployName, namespace = "default", options = {}) => {
const deployObj = await (0, exports.getCurrentDeployment)(deployName, namespace, options);
return deployObj.spec.template.spec.containers[0].image || "";
};
exports.getCurrentImageName = getCurrentImageName;
const getCurrentContainerEnvs = async (deployName, namespace = "default", options = {}) => {
const deployObj = await (0, exports.getCurrentDeployment)(deployName, namespace, options);
return deployObj.spec.template.spec.containers[0].env || {};
};
exports.getCurrentContainerEnvs = getCurrentContainerEnvs;
const extractWorkspaceSlugFromUrl = (url) => {
try {
return url.split("//")[1].split(".")[0];
}
catch (e) {
return;
}
};
exports.extractWorkspaceSlugFromUrl = extractWorkspaceSlugFromUrl;
const extractWorkspaceIdFromUser = (user) => {
const workspaceId = user.activeWorkspace._id
? mongodb_1.MongoDB.toString(user.activeWorkspace._id)
: mongodb_1.MongoDB.toString(user.activeWorkspace);
return workspaceId;
};
exports.extractWorkspaceIdFromUser = extractWorkspaceIdFromUser;
function getUnexpiredAccessToken(access_token) {
let expiredDate = (0, dayjs_1.default)("2999-12-31");
let expiredTimestamp = expiredDate.diff((0, dayjs_1.default)());
// assign "access_token" info to request:
const token = {
access_token,
expiredTimestamp: expiredTimestamp,
expiredDate: expiredDate.toDate(),
expiredDateGTM7: expiredDate.format("YYYY-MM-DD HH:mm:ss"),
};
return token;
}
exports.getUnexpiredAccessToken = getUnexpiredAccessToken;
const generateWorkspaceApiAccessToken = () => {
const name = (0, crypto_1.randomUUID)();
return { name, value: `${name}-${(0, crypto_1.randomUUID)()}` };
};
exports.generateWorkspaceApiAccessToken = generateWorkspaceApiAccessToken;