@catladder/cli
Version:
Panter cli tool for cloud CI/CD and DevOps
212 lines • 9.76 kB
JavaScript
;
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