@topgroup/diginext
Version:
A BUILD SERVER & CLI to deploy apps to any Kubernetes clusters.
328 lines (327 loc) • 16.3 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.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;