UNPKG

@topgroup/diginext

Version:

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

328 lines (327 loc) 16.3 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.pullOrCloneGitRepoHTTP = exports.injectAuthToRepoURL = exports.isValidBase64 = exports.isValidRepoURL = exports.validateRepoURL = exports.repoUrlToRepoSSH = exports.repoSshToRepoURL = exports.parseGitRepoDataFromRepoURL = exports.parseGitRepoDataFromRepoSSH = exports.stageCommitAndPushAll = exports.isUnstagedFiles = exports.getLatestTagOfGitRepo = exports.getCurrentGitBranch = void 0; const log_1 = require("diginext-utils/dist/xconsole/log"); const fs_1 = require("fs"); const lodash_1 = __importStar(require("lodash")); const path_1 = __importDefault(require("path")); const simple_git_1 = require("simple-git"); const plugins_1 = require("../../plugins"); /** * Get current git branch */ const getCurrentGitBranch = async (dir = process.cwd()) => { const git = (0, simple_git_1.simpleGit)(dir, { binary: "git" }); const status = await git.status(); const curBranch = status.current; return curBranch; }; exports.getCurrentGitBranch = getCurrentGitBranch; /** * Get latest tag of the git repository */ async function getLatestTagOfGitRepo(dir = process.cwd()) { const git = (0, simple_git_1.simpleGit)(dir, { binary: "git" }); const tags = (await git.tags(["--sort", "creatordate"])).all || []; const latestTag = tags.length > 0 ? (0, lodash_1.last)(tags) : await (0, exports.getCurrentGitBranch)(dir); return latestTag; } exports.getLatestTagOfGitRepo = getLatestTagOfGitRepo; async function isUnstagedFiles(dir = process.cwd()) { const git = (0, simple_git_1.simpleGit)(dir); try { const status = await git.status(); // Extract the list of unstaged files from the status object const unstagedFiles = status.files.filter((file) => file.index === "M" || file.working_dir === "M"); return unstagedFiles.length > 0; } catch (error) { return false; } } exports.isUnstagedFiles = isUnstagedFiles; /** * Stage all files, commit them & push to git origin. */ async function stageCommitAndPushAll(options) { const { directory = "./", message = "build(prepare): commit all files & push to origin" } = options; const git = (0, simple_git_1.simpleGit)(directory, { binary: "git" }); const gitStatus = await git.status(["-s"]); // log("[current branch]", gitStatus.current); const currentBranch = gitStatus.current; const currentBranchKebab = lodash_1.default.kebabCase(currentBranch); // commit & push everything, then try to merge "master" to current branch try { await git.pull("origin", currentBranch, ["--no-ff"]); await git.add("./*"); await git.commit(message); await git.push("origin", currentBranch); } catch (e) { (0, log_1.logError)(e); } return { currentBranch, currentBranchKebab }; } exports.stageCommitAndPushAll = stageCommitAndPushAll; /** * Read git data in a repo SSH url * @param {string} repoSSH - Example: `git@bitbucket.org:organization-name/git-repo-slug.git` */ function parseGitRepoDataFromRepoSSH(repoSSH) { let org, repoSlug, gitDomain, providerType; let fullSlug; try { org = repoSSH.split(":")[1].split("/")[0]; } catch (e) { (0, log_1.logError)(`Unable to parse "org": Repository SSH (${repoSSH}) is invalid`); return; } try { repoSlug = repoSSH.indexOf(".") > -1 ? repoSSH.split(":")[1].split("/")[1].split(".")[0] : repoSSH.split(":")[1].split("/")[1]; } catch (e) { (0, log_1.logError)(`Unable to parse "slug": Repository SSH (${repoSSH}) is invalid`); return; } try { gitDomain = repoSSH.split(":")[0].split("@")[1]; } catch (e) { (0, log_1.logError)(`Unable to parse "domain": Repository SSH (${repoSSH}) is invalid`); return; } try { providerType = gitDomain.split(".")[0]; } catch (e) { (0, log_1.logError)(`Unable to parse "provider": Repository SSH (${repoSSH}) is invalid`); return; } fullSlug = `${org}/${repoSlug}`; return { namespace: org, repoSlug, fullSlug, gitDomain, providerType }; } exports.parseGitRepoDataFromRepoSSH = parseGitRepoDataFromRepoSSH; /** * Read git data in a git repo url * @param {string} repoURL - Example: `https://bitbucket.org/organization-name/git-repo-slug` */ function parseGitRepoDataFromRepoURL(repoURL) { let namespace, repoSlug, gitDomain, providerType; let fullSlug; repoURL = (0, lodash_1.trimEnd)(repoURL, "/"); repoURL = (0, lodash_1.trimEnd)(repoURL, "#"); if (repoURL.indexOf(".git") > -1) repoURL = repoURL.substring(0, repoURL.indexOf(".git")); if (repoURL.indexOf("?") > -1) repoURL = repoURL.substring(0, repoURL.indexOf("?")); // console.log(repoURL); [gitDomain, namespace, repoSlug] = repoURL.split("://")[1].split("/"); try { providerType = gitDomain.split(".")[0]; } catch (e) { console.error(`Repository URL (${repoURL}) is invalid.`); return; } fullSlug = `${namespace}/${repoSlug}`; return { namespace, repoSlug, fullSlug, gitDomain, providerType }; } exports.parseGitRepoDataFromRepoURL = parseGitRepoDataFromRepoURL; /** * Generate git repo SSH url from a git repo URL * @example "git@github.com:digitopvn/diginext.git" -> "https://github.com/digitopvn/diginext" */ function repoSshToRepoURL(repoSSH) { const repoData = parseGitRepoDataFromRepoSSH(repoSSH); if (!repoData) throw new Error(`Unable to parse: ${repoSSH}`); return `https://${repoData.gitDomain}/${repoData.fullSlug}`; } exports.repoSshToRepoURL = repoSshToRepoURL; /** * Generate git repo URL from a git repo SSH url * @example "https://github.com/digitopvn/diginext" -> "git@github.com:digitopvn/diginext.git" */ function repoUrlToRepoSSH(repoURL) { const repoData = parseGitRepoDataFromRepoURL(repoURL); if (!repoData) throw new Error(`Unable to parse: ${repoURL}`); return `git@${repoData.gitDomain}:${repoData.fullSlug}.git`; } exports.repoUrlToRepoSSH = repoUrlToRepoSSH; function validateRepoURL(url) { if (!url) throw new Error(`Repo URL is empty.`); if (!(0, lodash_1.startsWith)(url, "https")) throw new Error(`Repo URL should start with "https".`); // if (!endsWith(url, ".git")) throw new Error(`Repo URL should end with ".git".`); } exports.validateRepoURL = validateRepoURL; function isValidRepoURL(url) { try { validateRepoURL(url); return true; } catch (e) { return false; } } exports.isValidRepoURL = isValidRepoURL; function isValidBase64(str) { const base64Regex = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; return base64Regex.test(str); } exports.isValidBase64 = isValidBase64; function injectAuthToRepoURL(url, options) { // validate validateRepoURL(url); if (!options.type) options.type = "Bearer"; if (!options.token) throw new Error(options.type === "Bearer" ? `Personal access token is required.` : "Token (username and password encoded in BASE64) is required."); // basic auth if (options.type === "Basic" && !isValidBase64(options.token)) throw new Error(`Token should be a valid Base64 encoded string.`); const basicAuth = options.type === "Basic" ? Buffer.from(options.token, "base64") : undefined; if (options.type === "Basic" && !basicAuth) throw new Error(`Token (username and password encoded in BASE64) is required.`); // repo data const repoData = parseGitRepoDataFromRepoURL(url); return options.type === "Bearer" ? `https://oauth2:${options.token}@${repoData.gitDomain}/${repoData.fullSlug}.git` : `https://${basicAuth}@${repoData.gitDomain}/${repoData.fullSlug}.git`; } exports.injectAuthToRepoURL = injectAuthToRepoURL; const pullOrCloneGitRepoHTTP = async (repoURL, dir, branch, options) => { var _a; let git; let success = false; if (!options.useAccessToken) throw new Error(`Access token is required to pull/clone repo with HTTPS.`); const repoUrlWithAuth = injectAuthToRepoURL(repoURL, { type: options.useAccessToken.type, token: options.useAccessToken.value }); if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("pullOrCloneGitRepoHTTP() > repoUrlWithAuth :>> ", repoUrlWithAuth); if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("pullOrCloneGitRepoHTTP() > repoURL :>> ", repoURL); if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("pullOrCloneGitRepoHTTP() > dir :>> ", dir); // if (options?.isDebugging) console.log("pullOrCloneGitRepoHTTP() > options :>> ", options); const onProgress = (event) => { if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("pullOrCloneGitRepoHTTP > event :>> ", event); const { method, stage, progress } = event || {}; 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 ((0, fs_1.existsSync)(dir)) { try { if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("pullOrCloneGitRepoHTTP() > directory exists :>> try to PULL..."); git = (0, simple_git_1.simpleGit)(dir, { progress: onProgress }); // ----------------------- // CAUTION: 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, `>`, { repoURL }); if (((_a = originRemote === null || originRemote === void 0 ? void 0 : originRemote.refs) === null || _a === void 0 ? void 0 : _a.fetch) !== repoURL) { await git.removeRemote("origin"); await git.addRemote("origin", repoUrlWithAuth); } const curBranch = await (0, exports.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, plugins_1.deleteFolderRecursive)(path_1.default.join(dir, ".git")); if (options === null || options === void 0 ? void 0 : options.removeCIOnFinish) await (0, plugins_1.deleteFolderRecursive)(path_1.default.join(dir, ".github")); success = true; } catch (e) { if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("pullOrCloneGitRepoHTTP() > 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 "${repoURL}" in "${dir}" directory (${e.message}) -> trying to clone new...`); try { // just for sure... await (0, plugins_1.deleteFolderRecursive)(dir); // for CLI create new app from a framework git = (0, simple_git_1.simpleGit)({ progress: onProgress }); await git.clone(repoUrlWithAuth, dir, [`--branch=${branch}`, "--single-branch", "--depth=1"]); if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("pullOrCloneGitRepoHTTP() > Success to CLONE !"); // remove git on finish if (options === null || options === void 0 ? void 0 : options.removeGitOnFinish) await (0, plugins_1.deleteFolderRecursive)(path_1.default.join(dir, ".git")); if (options === null || options === void 0 ? void 0 : options.removeCIOnFinish) await (0, plugins_1.deleteFolderRecursive)(path_1.default.join(dir, ".github")); success = true; } catch (e2) { if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("pullOrCloneGitRepoHTTP() > 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 SSH clone "${repoURL}" (${branch}) to "${dir}" directory: ${e2.message}`); throw new Error(`Failed to SSH clone "${repoURL}" (${branch}) to "${dir}" directory: ${e2.message}`); } } } else { if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("pullOrCloneGitRepoHTTP() > directory NOT exists :>> try to CLONE instead..."); if (options === null || options === void 0 ? void 0 : options.onUpdate) options === null || options === void 0 ? void 0 : options.onUpdate(`[HTTP] Cache source code not found. Cloning "${repoURL}" (${branch}) to "${dir}" directory.`); try { git = (0, simple_git_1.simpleGit)({ progress: onProgress }); await git.clone(repoUrlWithAuth, dir, [`--branch=${branch}`, "--single-branch", "--depth=1"]); if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("✅ pullOrCloneGitRepoHTTP() > Success to CLONE !"); // remove git on finish if (options === null || options === void 0 ? void 0 : options.removeGitOnFinish) await (0, plugins_1.deleteFolderRecursive)(path_1.default.join(dir, ".git")); if (options === null || options === void 0 ? void 0 : options.removeCIOnFinish) await (0, plugins_1.deleteFolderRecursive)(path_1.default.join(dir, ".github")); success = true; } catch (e) { if (options === null || options === void 0 ? void 0 : options.isDebugging) console.error(`❌ pullOrCloneGitRepoHTTP() > Failed to CLONE: ${e}`); if (options === null || options === void 0 ? void 0 : options.onUpdate) options === null || options === void 0 ? void 0 : options.onUpdate(`[HTTP] Failed to clone "${repoURL}" (${branch}) to "${dir}" directory: ${e}`); throw new Error(`[HTTP] Failed to clone "${repoURL}" (${branch}) to "${dir}" directory: ${e}`); } } return success; }; exports.pullOrCloneGitRepoHTTP = pullOrCloneGitRepoHTTP;