UNPKG

@catladder/pipeline

Version:

Panter workflow for cloud CI/CD and DevOps

467 lines (466 loc) • 18.8 kB
"use strict"; var __assign = this && this.__assign || function () { __assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = this && this.__awaiter || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = this && this.__generator || function (thisArg, body) { var _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __rest = this && this.__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __read = this && this.__read || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = this && this.__spreadArray || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createGitlabJobs = exports.makeGitlabJob = exports.GITLAB_ENVIRONMENT_URL_VARIABLE = void 0; var lodash_1 = require("lodash"); var getInjectVarsScript_1 = require("../../bash/getInjectVarsScript"); var defaults_1 = require("../../defaults"); var utils_1 = require("../../utils"); var gitlab_1 = require("../../utils/gitlab"); var removeUndefined_1 = require("../../utils/removeUndefined"); var BashExpression_1 = require("../../bash/BashExpression"); var cache_1 = require("./cache"); exports.GITLAB_ENVIRONMENT_URL_VARIABLE = "CL_GITLAB_ENVIRONMENT_URL"; var getFullJobName = function (_a) { var type = _a.type, name = _a.name, baseName = _a.baseName, allJobs = _a.allJobs, env = _a.env; var shouldAddIcon = allJobs.workspaces.length > 0; var icon = type === "component" ? "🔹" : type === "agent" ? "🤖" : "🔸"; var prefix = shouldAddIcon ? icon + " " : ""; if (env) { return "".concat(prefix).concat(baseName, " ").concat(name, " | ").concat(env, " "); } return "".concat(prefix).concat(baseName, " ").concat(name); }; var getFullReferencedJobNameFromComponent = function (referencedJobName, componentName, env, allJobs) { var _a, _b; var referencedJob = (_b = (_a = allJobs.components.find(function (j) { return j.context.name === componentName && j.context.env === env; })) === null || _a === void 0 ? void 0 : _a.jobs) === null || _b === void 0 ? void 0 : _b.find(function (j) { return j.name === referencedJobName; }); if (!referencedJob) { throw new Error("unknown job referenced: '".concat(referencedJobName, "' from '").concat(env, ":").concat(componentName, "'")); } return getFullJobName({ type: "component", name: referencedJobName, baseName: componentName, env: env, allJobs: allJobs }); }; var getFullReferencedJobNameFromWorkspace = function (referencedJobName, workspaceName, env, allJobs) { var _a, _b; var referencedJob = (_b = (_a = allJobs.workspaces.find(function (w) { return w.context.name === workspaceName; })) === null || _a === void 0 ? void 0 : _a.jobs) === null || _b === void 0 ? void 0 : _b.find(function (j) { return j.name === referencedJobName; }); if (!referencedJob) { throw new Error("unknown job referenced: '".concat(referencedJobName, "' from workspace ").concat(env, ":").concat(workspaceName, "'")); } return getFullJobName({ type: "workspace", name: referencedJobName, baseName: workspaceName, env: env, allJobs: allJobs }); }; var getJobName = function (need) { return (0, lodash_1.isObject)(need) ? need.job : need; }; var makeGitlabJob = function (context, job, allJobs, baseRules) { var _a, _b; var environment = job.environment, envMode = job.envMode, needsStages = job.needsStages, name = job.name, needs = job.needs, jobTags = job.jobTags, script = job.script, variables = job.variables, runnerVariables = job.runnerVariables, when = job.when, cache = job.cache, rest = __rest(job, ["environment", "envMode", "needsStages", "name", "needs", "jobTags", "script", "variables", "runnerVariables", "when", "cache"]); var stage = envMode === "stagePerEnv" && context.type !== "agent" ? "".concat(job.stage, " ").concat(context.env) : job.stage; var deduplicatedGitlabNeeds = getGitlabNeeds(context, job, allJobs); var fullJobName = getFullJobName({ type: context.type, name: name, baseName: context.name, env: context.type !== "agent" ? context.env : undefined, allJobs: allJobs }); // backwards compatibility, some may still use KUBERNETES_CPU_REQUEST, KUBERNETES_MEMORY_REQUEST, etc. in variables. // those should now be set in the runnerVariables as they don't work in the variables key of the catladder job, becuase those get injected var PIPELINE_RUNNER_VARIABLES = ["KUBERNETES_CPU_REQUEST", "KUBERNETES_MEMORY_REQUEST", "KUBERNETES_CPU_LIMIT", "KUBERNETES_MEMORY_LIMIT"]; // remove those from variables and add them to runnerVariables var varsInjectScripts = (0, gitlab_1.collapseableSection)("injectvars", "Injecting variables")(__spreadArray([], __read((0, getInjectVarsScript_1.getInjectVarsScript)( // remove legacy variables Object.fromEntries(Object.entries(variables !== null && variables !== void 0 ? variables : {}).filter(function (_a) { var _b = __read(_a, 1), key = _b[0]; return !PIPELINE_RUNNER_VARIABLES.includes(key); })))), false)); var legacyRunnerVariables = Object.fromEntries(Object.entries(variables !== null && variables !== void 0 ? variables : {}).filter(function (_a) { var _b = __read(_a, 1), key = _b[0]; return PIPELINE_RUNNER_VARIABLES.includes(key); })); if (Object.keys(legacyRunnerVariables).length > 0) { console.warn("Legacy variables detected in ".concat(fullJobName, ": ").concat(Object.keys(legacyRunnerVariables).join(", "), ". Please move them to the runnerVariables key.")); } var rules = __spreadArray(__spreadArray([], __read((_a = job.rules) !== null && _a !== void 0 ? _a : []), false), __read(baseRules ? baseRules.map(function (rule) { return __assign({ when: when }, rule); }) : when ? [{ when: when }] : []), false); var gitlabJob = __assign(__assign({ retry: defaults_1.BASE_RETRY, interruptible: true }, rest), { cache: cache ? (0, cache_1.addCacheFallback)(cache, context) : undefined, rules: rules.length > 0 ? rules : undefined, variables: __assign(__assign({}, legacyRunnerVariables), runnerVariables), script: __spreadArray(__spreadArray([], __read(varsInjectScripts), false), __read((_b = script === null || script === void 0 ? void 0 : script.filter(utils_1.notNil)) !== null && _b !== void 0 ? _b : []), false), tags: jobTags, stage: stage, // sort in a predictable manner for snapshot tests needs: deduplicatedGitlabNeeds }); var modified = addGitlabEnvironment(context, environment, gitlabJob, allJobs); return [fullJobName, (0, removeUndefined_1.removeUndefined)(modified)]; }; exports.makeGitlabJob = makeGitlabJob; var addGitlabEnvironment = function (context, catladderJobEnvironment, job, allJobs) { var _a, _b; if (!catladderJobEnvironment) { return job; } if (context.type !== "component") { // don't add enviornment for workspace and agent jobs atm. return job; } var env = context.env, name = context.name, environment = context.environment; var envVars = environment.envVars, envType = environment.envType; var on_stop = catladderJobEnvironment.on_stop, restEnvironment = __rest(catladderJobEnvironment, ["on_stop"]); // those can be dynamic, so we therefore have to do this: https://docs.gitlab.com/ee/ci/environments/#set-a-dynamic-environment-url var dotEnvFile = "gitlab_environment.env"; var createsJobEnv = !catladderJobEnvironment.action || catladderJobEnvironment.action === "start"; var artifacts = (0, lodash_1.merge)((_a = job.artifacts) !== null && _a !== void 0 ? _a : {}, createsJobEnv ? { reports: { dotenv: dotEnvFile } } : {}); var scriptToAdd = ["echo \"".concat(exports.GITLAB_ENVIRONMENT_URL_VARIABLE, "=").concat((0, BashExpression_1.getBashVariable)("ROOT_URL"), "\" >> ").concat(dotEnvFile)]; // this is NOT a bashVariable since it NEEDS to be used as a string in gitlab var gitlabEnvironmentName = envType === "review" ? "".concat(env, "/$CI_COMMIT_REF_NAME/").concat(name) // FIXME: should be replaced with mr name as well : "".concat(env, "/").concat(name); return __assign(__assign(__assign(__assign({}, job), { environment: __assign(__assign(__assign({ name: gitlabEnvironmentName }, createsJobEnv ? { url: "$".concat(exports.GITLAB_ENVIRONMENT_URL_VARIABLE) } : {}), on_stop ? { on_stop: getFullReferencedJobNameFromComponent(on_stop, name, env, allJobs) } : {}), restEnvironment) }), !(0, lodash_1.isEmpty)(artifacts) ? { artifacts: artifacts } : {}), { script: __spreadArray(__spreadArray([], __read((_b = job.script) !== null && _b !== void 0 ? _b : []), false), __read(createsJobEnv ? scriptToAdd : []), false) }); }; var createGitlabJobs = function (allJobs, baseRules) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, __spreadArray(__spreadArray(__spreadArray([], __read(allJobs.workspaces), false), __read(allJobs.components), false), __read(allJobs.agents), false).flatMap(function (_a) { var context = _a.context, jobs = _a.jobs; return jobs.map(function (job) { var _a = __read((0, exports.makeGitlabJob)(context, job, allJobs, baseRules), 2), fullJobName = _a[0], gitlabJob = _a[1]; return { name: fullJobName, gitlabJob: gitlabJob, context: context }; }); })]; }); }); }; exports.createGitlabJobs = createGitlabJobs; function getGitlabNeeds(context, job, allJobs) { var _a; var needs = context.type === "workspace" ? getGitlabNeedsForWorkspaceJob(context, job, allJobs) : context.type === "agent" ? (_a = job.needs) !== null && _a !== void 0 ? _a : null : getGitlabNeedsForComponentJob(context, job, allJobs); return needs ? deduplicateNeeds(needs) : undefined; } function deduplicateNeeds(needs) { return needs ? __spreadArray([], __read(new Map(needs.map(function (n) { return [(0, lodash_1.isObject)(n) ? n.job : n, n]; })).values()), false) : undefined; } function getGitlabNeedsForComponentJob(context, _a, allJobs) { var needsStages = _a.needsStages, needs = _a.needs; var needsFromStages = needsStages === null || needsStages === void 0 ? void 0 : needsStages.flatMap(function (n) { var _a, _b, _c, _d, _e, _f, _g, _h; var componentName = context.name; if (!n.workspaceName) { var allJobNamesFromThatStage = (_c = (_b = (_a = allJobs.components.filter(function (j) { return j.context.name === componentName && j.context.env === context.env; }).flatMap(function (j) { return j.jobs; })) === null || _a === void 0 ? void 0 : _a.filter(function (j) { return j.stage === n.stage; })) === null || _b === void 0 ? void 0 : _b.map(function (j) { return j.name; })) !== null && _c !== void 0 ? _c : []; return allJobNamesFromThatStage.map(function (job) { var _a; return { job: job, artifacts: (_a = n.artifacts) !== null && _a !== void 0 ? _a : false, componentName: componentName }; }); } else { var allJobNamesFromThatStage = (_h = (_g = (_f = (_e = (_d = allJobs.workspaces.find(function (w) { return w.context.name === n.workspaceName && w.context.env === context.env; })) === null || _d === void 0 ? void 0 : _d.jobs) === null || _e === void 0 ? void 0 : _e.flatMap(function (j) { return j; })) === null || _f === void 0 ? void 0 : _f.filter(function (j) { return j.stage === n.stage; })) === null || _g === void 0 ? void 0 : _g.map(function (j) { return j.name; })) !== null && _h !== void 0 ? _h : []; return allJobNamesFromThatStage.map(function (job) { var _a; return { job: job, artifacts: (_a = n.artifacts) !== null && _a !== void 0 ? _a : false, workspaceName: n.workspaceName }; }); } }); var cleanedNeeds = __spreadArray(__spreadArray([], __read(needsFromStages !== null && needsFromStages !== void 0 ? needsFromStages : []), false), __read(needs !== null && needs !== void 0 ? needs : []), false); return cleanedNeeds === null || cleanedNeeds === void 0 ? void 0 : cleanedNeeds.map(function (n) { var _a; return (0, lodash_1.isObject)(n) ? "workspaceName" in n ? { job: getFullReferencedJobNameFromWorkspace(n.job, n.workspaceName, context.env, allJobs), artifacts: n.artifacts } : { job: getFullReferencedJobNameFromComponent(n.job, (_a = n.componentName) !== null && _a !== void 0 ? _a : context.name, context.env, allJobs), artifacts: n.artifacts } : getFullReferencedJobNameFromComponent(n, context.name, context.env, allJobs); }).sort(function (a, b) { return getJobName(a).localeCompare(getJobName(b)); }); } /** * *unclear whether we actually need this. So far jobs in a workspace don't have needs to other jobs from the same workspace */ function getGitlabNeedsForWorkspaceJob(context, _a, allJobs) { var needsStages = _a.needsStages, needs = _a.needs; var needsFromStages = needsStages === null || needsStages === void 0 ? void 0 : needsStages.flatMap(function (n) { var _a, _b, _c, _d; var workspaceName = (_a = n.workspaceName) !== null && _a !== void 0 ? _a : context.name; var allJobNamesFromThatStage = (_d = (_c = (_b = allJobs.workspaces.filter(function (j) { return j.context.name === workspaceName && j.context.env === context.env; }).flatMap(function (j) { return j.jobs; })) === null || _b === void 0 ? void 0 : _b.filter(function (j) { return j.stage === n.stage; })) === null || _c === void 0 ? void 0 : _c.map(function (j) { return j.name; })) !== null && _d !== void 0 ? _d : []; return allJobNamesFromThatStage.map(function (job) { var _a; return { job: job, artifacts: (_a = n.artifacts) !== null && _a !== void 0 ? _a : false, workspaceName: workspaceName }; }); }); var cleanedNeeds = __spreadArray(__spreadArray([], __read(needsFromStages !== null && needsFromStages !== void 0 ? needsFromStages : []), false), __read(needs !== null && needs !== void 0 ? needs : []), false); return cleanedNeeds === null || cleanedNeeds === void 0 ? void 0 : cleanedNeeds.map(function (n) { return (0, lodash_1.isObject)(n) ? { job: getFullReferencedJobNameFromWorkspace(n.job, "workspaceName" in n && n.workspaceName ? n.workspaceName : context.name, context.env, allJobs), artifacts: n.artifacts } : getFullReferencedJobNameFromWorkspace(n, context.name, context.env, allJobs); }).sort(function (a, b) { return getJobName(a).localeCompare(getJobName(b)); }); }