UNPKG

@catladder/cli

Version:

Panter cli tool for cloud CI/CD and DevOps

212 lines 9.76 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.upsertAllVariables = exports.clearBackups = exports.getVariableValueByRawName = exports.getAllVariables = exports.getProjectInfo = exports.doGitlabRequest = exports.getGitlabToken = exports.setupGitlabToken = exports.hasGitlabToken = void 0; const pipeline_1 = require("../../../pipeline/src/index.js"); const lodash_1 = require("lodash"); const memoizee_1 = __importDefault(require("memoizee")); const node_fetch_1 = __importDefault(require("node-fetch")); const open_1 = __importDefault(require("open")); const preferences_1 = require("./preferences"); const gitProjectInformation_1 = require("../git/gitProjectInformation"); const TOKEN_KEY = "gitlab-personal-access-token"; const hasGitlabToken = async () => await (0, preferences_1.hasPreference)(TOKEN_KEY); exports.hasGitlabToken = hasGitlabToken; const setupGitlabToken = async (vorpal) => { vorpal.log(""); vorpal.log("☝ in order to access the api, we need a personal access token"); vorpal.log("Its best to create one specifically for catladder"); vorpal.log("Scopes needed: api"); vorpal.log(""); vorpal.log("☝ we open up the settings page for you!"); vorpal.log(""); const [{ shouldContinue }, { gitRemoteHost }] = await Promise.all([ vorpal.prompt({ default: true, message: "Ok", name: "shouldContinue", type: "prompt", }), (0, gitProjectInformation_1.getGitRemoteHostAndPath)(), ]); (0, open_1.default)(`https://${gitRemoteHost}/-/user_settings/personal_access_tokens`); vorpal.log("Please type in gitlab's personal access token"); const { personalToken } = await vorpal.prompt({ type: "string", name: "personalToken", default: "", message: "Your personal access token ", }); if (personalToken) { await (0, preferences_1.setPreference)(TOKEN_KEY, personalToken); } }; exports.setupGitlabToken = setupGitlabToken; const getGitlabToken = async (vorpal) => { if (!(await (0, exports.hasGitlabToken)())) { if (!vorpal) { console.error("⚠️ gitlab token missing, please run catladder to set it up"); process.exit(1); } await (0, exports.setupGitlabToken)(vorpal); } return (0, preferences_1.getPreference)(TOKEN_KEY); }; exports.getGitlabToken = getGitlabToken; const doGitlabRequest = async (vorpal, path, data = undefined, method = "GET") => { const [rootToken, { gitRemoteHost }] = await Promise.all([ (0, exports.getGitlabToken)(vorpal), (0, gitProjectInformation_1.getGitRemoteHostAndPath)(), ]); //const method = data ? (update ? "PUT" : "POST") : "GET"; const result = await (0, node_fetch_1.default)(`https://${gitRemoteHost}/api/v4/${path}`, { method, headers: { "Content-Type": "application/json", "Private-Token": rootToken, }, body: data ? JSON.stringify(data) : undefined, }); if (result.status >= 200 && result.status < 400) { if (result.headers.get("content-type") === "application/json") { return result.json(); } return null; } if (result.status === 404) { throw new Error("not found"); } throw new Error(`Could not send request to gitlab api ${path}: ${result.status} "${result.statusText}".\nResponse: ${JSON.stringify(await result.json(), null, 2)}`); }; exports.doGitlabRequest = doGitlabRequest; const getProjectInfo = async (vorpal) => { const { gitRemotePath } = await (0, gitProjectInformation_1.getGitRemoteHostAndPath)(); const project = await (0, exports.doGitlabRequest)(vorpal, `projects/${encodeURIComponent(gitRemotePath)}`); return project; }; exports.getProjectInfo = getProjectInfo; exports.getAllVariables = (0, memoizee_1.default)(async (vorpal, n = 5) => { const { id } = await (0, exports.getProjectInfo)(vorpal); let all = []; let result = []; let page = 1; do { // Create an array of promises for N pages const promises = Array.from({ length: n }, (_, i) => { return (0, exports.doGitlabRequest)(vorpal, `projects/${id}/variables?per_page=100&page=${page + i}`); }); // Wait for all promises to resolve result = await Promise.all(promises); // Increment the page by N page += n; // Flatten the result array and add it to 'all' all = [...all, ...result.flat()]; // Continue only if the last page had results } while (result.length > 0 && result[result.length - 1].length > 0); return all; }, { promise: true }); const getVariableValueByRawName = async (vorpal, rawName) => { var _a; const allVariables = await (0, exports.getAllVariables)(vorpal); return (_a = allVariables.find((v) => v.key === rawName)) === null || _a === void 0 ? void 0 : _a.value; }; exports.getVariableValueByRawName = getVariableValueByRawName; const maskableRegex = new RegExp("^[a-zA-Z0-9_+=/@:.~-]{8,}$"); // SEE https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js#L20 const isMaskable = (value) => maskableRegex.test(value); const createVariable = async (vorpal, projectId, key, value, masked = true, environment_scope = "*") => { return await (0, exports.doGitlabRequest)(vorpal, `projects/${projectId}/variables`, { key, value, masked: masked && isMaskable(value), environment_scope, }, "POST"); }; const updateVariable = async (vorpal, projectId, key, value, masked = true) => { return await (0, exports.doGitlabRequest)(vorpal, `projects/${projectId}/variables/${key}`, { value, masked: masked && isMaskable(value), }, "PUT"); }; const deleteVariable = async (vorpal, projectId, key) => { return await (0, exports.doGitlabRequest)(vorpal, `projects/${projectId}/variables/${key}`, undefined, "DELETE"); }; const getAllCatladderEnvVarsInGitlab = async (vorpal) => { const allVariables = await (0, exports.getAllVariables)(vorpal).then((v) => v.reduce((acc, variable) => { var _a, _b, _c, _d; const { key } = variable; if (key.startsWith("CL_")) { const matchBackup = key.match(/(CL_.*)_backup_([0-9]+)/); if (matchBackup) { const key = matchBackup[1]; const timestamp = Number(matchBackup[2]); const backups = [...((_b = (_a = acc[key]) === null || _a === void 0 ? void 0 : _a.backups) !== null && _b !== void 0 ? _b : []), timestamp]; return { ...acc, [key]: { ...((_c = acc[key]) !== null && _c !== void 0 ? _c : {}), // add value backups, }, }; } return { ...acc, [key]: { backups: [], ...((_d = acc[key]) !== null && _d !== void 0 ? _d : {}), // may add backups value: variable.value, }, }; } return acc; }, {})); return allVariables; }; const getBackupKey = (fullKey, timestamp) => `${fullKey}_backup_${timestamp}`; const clearBackups = async (vorpal, keep) => { const existingVariables = await getAllCatladderEnvVarsInGitlab(vorpal); const { id } = await (0, exports.getProjectInfo)(vorpal); for (const [key, { backups }] of Object.entries(existingVariables)) { const backupsSorted = backups.sort((a, b) => b - a); //const toKeep = backupsSorted.slice(0, keep); const toDelete = backupsSorted.slice(keep); for (const timestamp of toDelete) { await deleteVariable(vorpal, id, getBackupKey(key, timestamp)); } } }; exports.clearBackups = clearBackups; const upsertAllVariables = async (vorpal, variables, env, componentName, backup = true, masked = true) => { var _a; const { id } = await (0, exports.getProjectInfo)(vorpal); // we list all existing variables. We also gather backup versions, but its currently unused const existingVariables = await getAllCatladderEnvVarsInGitlab(vorpal); for (const [key, value] of Object.entries(variables !== null && variables !== void 0 ? variables : {})) { const fullKey = (0, pipeline_1.getSecretVarName)(env, componentName, key); const valueSanitized = (0, lodash_1.isObject)(value) ? JSON.stringify(value) : `${value}`; const exists = (0, lodash_1.has)(existingVariables, fullKey); const oldValue = (_a = existingVariables[fullKey]) === null || _a === void 0 ? void 0 : _a.value; const changed = oldValue !== valueSanitized; if (changed) { if (exists) { vorpal.log(`changed: ${key}`); await updateVariable(vorpal, id, fullKey, valueSanitized, masked); // write backup if (backup) { await createVariable(vorpal, id, getBackupKey(fullKey, new Date().getTime()), oldValue, masked, "_backup"); } } else { vorpal.log(`new : ${key}`); await createVariable(vorpal, id, fullKey, valueSanitized, masked); } } else { vorpal.log(`skip : ${key}`); } } exports.getAllVariables.clear(); }; exports.upsertAllVariables = upsertAllVariables; //# sourceMappingURL=gitlab.js.map