gitlab-ci-local
Version:
Tired of pushing to test your .gitlab-ci.yml?
357 lines • 67.1 kB
JavaScript
import chalk from "chalk";
import path from "path";
import deepExtend from "deep-extend";
import fs from "fs-extra";
import * as yaml from "js-yaml";
import prettyHrtime from "pretty-hrtime";
import { Job } from "./job.js";
import * as DataExpander from "./data-expander.js";
import { Utils } from "./utils.js";
import assert from "assert";
import { Validator } from "./validator.js";
import * as parallel from "./parallel.js";
import { GitData } from "./git-data.js";
import { ParserIncludes } from "./parser-includes.js";
import { Producers } from "./producers.js";
import { VariablesFromFiles } from "./variables-from-files.js";
import { init as initPredefinedVariables } from "./predefined-variables.js";
const MAX_FUNCTIONS = 3;
const INCLUDE_INPUTS_SUPPORTED_TYPES = ["string", "boolean", "number", "array"];
export class Parser {
_stages = [];
_gitlabData;
_jobNamePad = null;
jobs;
argv;
writeStreams;
pipelineIid;
expandVariables;
constructor(argv, writeStreams, pipelineIid, jobs, expandVariables) {
this.argv = argv;
this.writeStreams = writeStreams;
this.pipelineIid = pipelineIid;
this.jobs = jobs;
this.expandVariables = expandVariables;
}
get stages() {
return this._stages;
}
get gitlabData() {
return this._gitlabData;
}
get jobNamePad() {
return this._jobNamePad ?? 0;
}
static async create(argv, writeStreams, pipelineIid, jobs, expandVariables = true) {
const parser = new Parser(argv, writeStreams, pipelineIid, jobs, expandVariables);
const time = process.hrtime();
await parser.init();
const warnings = await Validator.run(parser.jobs, parser.stages);
for (const job of parser.jobs) {
if (job.artifacts === null) {
job.deleteArtifacts();
}
}
const parsingTime = process.hrtime(time);
const pathToExpandedGitLabCi = path.join(argv.cwd, argv.stateDir, "expanded-gitlab-ci.yml");
fs.mkdirpSync(path.join(argv.cwd, argv.stateDir));
fs.writeFileSync(pathToExpandedGitLabCi, yaml.dump(parser.gitlabData));
if (argv.childPipelineDepth == 0)
writeStreams.stderr(chalk `{grey parsing and downloads finished in ${prettyHrtime(parsingTime)}.}\n`);
for (const warning of warnings) {
writeStreams.stderr(chalk `{yellow ${warning}}\n`);
}
// # Second layer of check for errors that are not caught in Validator.run
if (parser.argv.jsonSchemaValidation) {
const time = process.hrtime();
Validator.jsonSchemaValidation({
pathToExpandedGitLabCi,
gitLabCiConfig: parser.gitlabData,
argv,
});
if (argv.childPipelineDepth == 0)
writeStreams.stderr(chalk `{grey json schema validated in ${prettyHrtime(process.hrtime(time))}}\n`);
}
return parser;
}
async init() {
const argv = this.argv;
const cwd = argv.cwd;
const stateDir = argv.stateDir;
const writeStreams = this.writeStreams;
const file = argv.file;
const pipelineIid = this.pipelineIid;
const fetchIncludes = argv.fetchIncludes;
const gitData = await GitData.init(cwd, writeStreams);
const variablesFromFiles = await VariablesFromFiles.init(argv, writeStreams, gitData);
const envMatchedVariables = Utils.findEnvMatchedVariables(variablesFromFiles);
const predefinedVariables = initPredefinedVariables({ gitData, argv, envMatchedVariables });
const variables = { ...predefinedVariables, ...envMatchedVariables, ...argv.variable };
const expanded = Utils.expandVariables(variables);
let yamlDataList = [{ stages: [".pre", "build", "test", "deploy", ".post"] }];
const gitlabCiData = await Parser.loadYaml(`${cwd}/${file}`, {}, this.expandVariables);
yamlDataList = yamlDataList.concat(await ParserIncludes.init(gitlabCiData, { argv, cwd, stateDir, writeStreams, gitData, fetchIncludes, variables: expanded, expandVariables: this.expandVariables, maximumIncludes: argv.maximumIncludes }));
ParserIncludes.resetCount();
const gitlabCiLocalData = await Parser.loadYaml(`${cwd}/.gitlab-ci-local.yml`, {}, this.expandVariables);
yamlDataList = yamlDataList.concat(await ParserIncludes.init(gitlabCiLocalData, { argv, cwd, stateDir, writeStreams, gitData, fetchIncludes, variables: expanded, expandVariables: this.expandVariables, maximumIncludes: argv.maximumIncludes }));
ParserIncludes.resetCount();
const gitlabData = deepExtend({}, ...yamlDataList);
// Expand various fields in gitlabData
DataExpander.jobExtends(gitlabData);
DataExpander.reference(gitlabData, gitlabData);
DataExpander.flattenLists(gitlabData);
DataExpander.transformDeprecatedGlobalDefaultSyntax(gitlabData);
DataExpander.inheritDefault(gitlabData);
DataExpander.normalize(gitlabData);
assert(gitlabData.stages && Array.isArray(gitlabData.stages), chalk `{yellow stages:} must be an array`);
if (!gitlabData.stages.includes(".pre")) {
gitlabData.stages.unshift(".pre");
}
if (!gitlabData.stages.includes(".post")) {
gitlabData.stages.push(".post");
}
this._stages = gitlabData.stages;
// Check job variables for invalid hash of key value pairs, and cast numbers to strings
Utils.forEachRealJob(gitlabData, (jobName, jobData) => {
assert(jobData.when !== "never", chalk `This GitLab CI configuration is invalid: jobs:${jobName} when:never can only be used in a rules section or workflow:rules`);
for (const [key, value] of Object.entries(jobData.variables ?? {})) {
jobData.variables[key] = Utils.normalizeVariables(value);
}
for (let i = 0; i < (jobData.services ?? []).length; i++) {
const service = jobData.services[i];
for (const [key, value] of Object.entries(service.variables || {})) {
assert(typeof value === "string" || typeof value === "number" || typeof value === "boolean", chalk `{blueBright ${jobName}.services[${i}]} has invalid variables hash of key value pairs. ${key}=${value}`);
jobData.services[i].variables[key] = String(value);
}
}
});
this._gitlabData = gitlabData;
// Generate jobs and put them into stages
Utils.forEachRealJob(gitlabData, (jobName, jobData) => {
assert(gitData != null, "gitData must be set");
assert(variablesFromFiles != null, "homeVariables must be set");
let nodeIndex = 1;
const parallelMatrixVariablesList = parallel.matrixVariablesList(jobData, jobName);
for (const parallelMatrixVariables of parallelMatrixVariablesList) {
let matrixJobName = jobName;
if (parallelMatrixVariables) {
matrixJobName = `${jobName}: [${Object.values(parallelMatrixVariables ?? []).join(",")}]`;
}
else if (parallel.isPlainParallel(jobData)) {
matrixJobName = `${jobName}: [${nodeIndex}/${parallelMatrixVariablesList.length}]`;
}
const job = new Job({
argv,
writeStreams,
data: jobData,
name: matrixJobName,
baseName: jobName,
globalVariables: gitlabData.variables,
pipelineIid: pipelineIid,
predefinedVariables: { ...predefinedVariables }, // NOTE: pass by value because predefinedVariables is mutated in the constructor
gitData,
variablesFromFiles,
matrixVariables: parallelMatrixVariables,
nodeIndex: (jobData.parallel != null) ? nodeIndex : null,
nodesTotal: parallelMatrixVariablesList.length,
expandVariables: this.expandVariables,
});
const foundStage = this.stages.includes(job.stage);
assert(foundStage, chalk `{yellow stage:${job.stage}} not found for {blueBright ${job.name}}`);
this.jobs.push(job);
nodeIndex++;
}
});
// Add some padding so that job logs are nicely aligned
// allow users to override this in case they have really long job name (see #840)
if (this.argv.maxJobNamePadding !== null && this.argv.maxJobNamePadding <= 0) {
this._jobNamePad = 0;
}
else {
const jobs = this.argv.job.length !== 0 ? this.argv.job : this.jobs;
jobs.forEach((job) => {
let jobNeedsLength = [];
if (this.argv.needs && this.argv.job.length > 0) {
const found = this.jobs.find(j => j.baseName === job);
if (found?.needs) {
jobNeedsLength = found.needs.map(f => f.job.length);
}
}
const jobLength = typeof job == "string" ? job.length : job.name.length;
this._jobNamePad = Math.max(jobLength, this._jobNamePad ?? 0, ...jobNeedsLength);
});
if (this.argv.maxJobNamePadding !== null) {
this._jobNamePad = Math.min(this.argv.maxJobNamePadding ?? 0, this._jobNamePad ?? 0);
}
}
// Set jobNamePad on all jobs
this.jobs.forEach((job) => {
job.jobNamePad = this.jobNamePad;
});
// Generate producers for each job
this.jobs.forEach((job) => {
job.producers = Producers.init(this.jobs, this.stages, job);
});
}
static async loadYaml(filePath, ctx = {}, expandVariables = true) {
const ymlPath = `${filePath}`;
if (!fs.existsSync(ymlPath)) {
return {};
}
const fileContent = await fs.readFile(`${filePath}`, "utf8");
const fileSplit = fileContent.split(/\r?\n/g);
const fileSplitClone = fileSplit.slice();
let interactiveMatch = null;
let descriptionMatch = null;
let injectSSHAgent = null;
let noArtifactsToSourceMatch = null;
let index = 0;
if (expandVariables) {
for (const line of fileSplit) {
interactiveMatch = interactiveMatch ?? /#\s?@\s?[Ii]nteractive/.exec(line);
injectSSHAgent = injectSSHAgent ?? /#\s?@\s?[Ii]njectSSHAgent/.exec(line);
noArtifactsToSourceMatch = noArtifactsToSourceMatch ?? /#\s?@\s?NoArtifactsToSource/i.exec(line);
descriptionMatch = descriptionMatch ?? /#\s?@\s?[Dd]escription (?<description>.*)/.exec(line);
const jobMatch = /\w:/.exec(line);
if (jobMatch && (interactiveMatch || descriptionMatch || injectSSHAgent || noArtifactsToSourceMatch)) {
if (interactiveMatch) {
fileSplitClone.splice(index + 1, 0, " gclInteractive: true");
index++;
}
if (injectSSHAgent) {
fileSplitClone.splice(index + 1, 0, " gclInjectSSHAgent: true");
index++;
}
if (noArtifactsToSourceMatch) {
fileSplitClone.splice(index + 1, 0, " gclArtifactsToSource: false");
index++;
}
if (descriptionMatch) {
fileSplitClone.splice(index + 1, 0, ` gclDescription: ${descriptionMatch?.groups?.description ?? ""}`);
index++;
}
interactiveMatch = null;
descriptionMatch = null;
injectSSHAgent = null;
noArtifactsToSourceMatch = null;
}
index++;
}
}
const referenceType = new yaml.Type("!reference", {
kind: "sequence",
construct: function (data) {
return { referenceData: data };
},
});
const schema = yaml.DEFAULT_SCHEMA.extend([referenceType]);
let fileData;
try {
fileData = yaml.loadAll(fileSplitClone.join("\n"), null, { schema });
}
catch (e) {
if (e instanceof yaml.YAMLException && e.reason === "duplicated mapping key") {
console.log(chalk `{black.bgYellowBright WARN } duplicated mapping key detected! Values will be overwritten!`);
fileData = yaml.loadAll(fileSplitClone.join("\n"), null, { schema, json: true });
}
else {
throw e;
}
}
if (fileData.length <= 1)
return fileData[0];
if (isGitlabSpecFile(fileData[0])) {
const inputsSpecification = fileData[0];
const uninterpolatedConfigurations = fileData[1];
const interpolatedConfigurations = JSON.stringify(uninterpolatedConfigurations)
.replace(/(?<firstChar>.)?(?<secondChar>.)?\$\[\[\s*inputs.(?<interpolationKey>[\w-]+)\s*\|?\s*(?<interpolationFunctions>.*?)\s*\]\](?<lastChar>[^$])?/g // https://regexr.com/81c16
, (_, firstChar, secondChar, interpolationKey, interpolationFunctions, lastChar) => {
const configFilePath = path.relative(process.cwd(), filePath);
const context = {
interpolationKey,
interpolationFunctions,
inputsSpecification,
configFilePath,
...ctx,
};
firstChar ??= "";
secondChar ??= "";
lastChar ??= "";
const { inputValue, inputType } = parseIncludeInputs(context);
const firstTwoChar = firstChar + secondChar;
switch (inputType) {
case "array":
if ((secondChar == "\"" && lastChar == "\"") && firstChar != "\\") {
return firstChar + JSON.stringify(inputValue);
}
// NOTE: This behaves slightly differently from gitlab.com. I can't come up with practical use case so i don't think it's worth the effort to mimic this
return firstTwoChar + JSON.stringify(JSON.stringify(inputValue)).slice(1, -1) + lastChar;
case "string":
return firstTwoChar +
JSON.stringify(inputValue) // ensure a valid json string
.slice(1, -1) + // remove the surrounding "
lastChar;
case "number":
case "boolean":
if ((secondChar == "\"" && lastChar == "\"") && firstChar != "\\") {
return firstChar + inputValue;
}
return firstTwoChar + inputValue + lastChar;
default:
Utils.switchStatementExhaustiveCheck(inputType);
}
});
return JSON.parse(interpolatedConfigurations);
}
return fileData[0];
}
}
function isGitlabSpecFile(fileData) {
return "spec" in fileData;
}
function validateInterpolationKey(ctx) {
const { configFilePath, interpolationKey, inputsSpecification } = ctx;
const invalidInterpolationKeyErr = chalk `This GitLab CI configuration is invalid: \`{blueBright ${configFilePath}}\`: unknown interpolation key: \`${interpolationKey}\`.`;
assert(inputsSpecification.spec.inputs?.[interpolationKey] !== undefined, invalidInterpolationKeyErr);
}
function validateInterpolationFunctions(ctx) {
const { interpolationFunctions, configFilePath } = ctx;
if (interpolationFunctions != "") {
console.log(chalk `{black.bgYellowBright WARN } interpolation functions is currently not supported via gitlab-ci-local. Functions will just be a no-op.`);
}
assert(interpolationFunctions.split("|").length <= MAX_FUNCTIONS, chalk `This GitLab CI configuration is invalid: \`{blueBright ${configFilePath}}\`: too many functions in interpolation block.`);
}
function validateInput(ctx) {
const { configFilePath, interpolationKey, inputsSpecification } = ctx;
const inputValue = getInputValue(ctx);
const options = inputsSpecification.spec.inputs[interpolationKey]?.options;
if (options) {
assert(options.includes(inputValue), chalk `This GitLab CI configuration is invalid: \`{blueBright ${configFilePath}}\`: \`{blueBright ${interpolationKey}}\` input: \`{blueBright ${inputValue}}\` cannot be used because it is not in the list of allowed options.`);
}
const expectedInputType = getExpectedInputType(ctx);
assert(INCLUDE_INPUTS_SUPPORTED_TYPES.includes(expectedInputType), chalk `This GitLab CI configuration is invalid: \`{blueBright ${configFilePath}}\`: header:spec:inputs:{blueBright ${interpolationKey}} input type unknown value: {blueBright ${expectedInputType}}.`);
const inputType = Array.isArray(inputValue) ? "array" : typeof inputValue;
assert(inputType === expectedInputType, chalk `This GitLab CI configuration is invalid: \`{blueBright ${configFilePath}}\`: \`{blueBright ${interpolationKey}}\` input: provided value is not a {blueBright ${expectedInputType}}.`);
const regex = inputsSpecification.spec.inputs[interpolationKey]?.regex;
if (regex) {
console.log(chalk `{black.bgYellowBright WARN } spec:inputs:regex is currently not supported via gitlab-ci-local. This will just be a no-op.`);
}
}
function parseIncludeInputs(ctx) {
validateInterpolationKey(ctx);
validateInterpolationFunctions(ctx);
validateInput(ctx);
return { inputValue: getInputValue(ctx), inputType: getExpectedInputType(ctx) };
}
function getInputValue(ctx) {
const { inputs, interpolationKey, configFilePath, inputsSpecification } = ctx;
const inputValue = inputs?.[interpolationKey] ??
inputsSpecification.spec.inputs[interpolationKey]?.default;
assert(inputValue !== undefined, chalk `This GitLab CI configuration is invalid: \`{blueBright ${configFilePath}}\`: \`{blueBright ${interpolationKey}}\` input: required value has not been provided.`);
return inputValue;
}
function getExpectedInputType(ctx) {
const { interpolationKey, inputsSpecification } = ctx;
return inputsSpecification.spec.inputs[interpolationKey]?.type || "string";
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyc2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsicGFyc2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxNQUFNLE9BQU8sQ0FBQztBQUMxQixPQUFPLElBQUksTUFBTSxNQUFNLENBQUM7QUFDeEIsT0FBTyxVQUFVLE1BQU0sYUFBYSxDQUFDO0FBQ3JDLE9BQU8sRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUMxQixPQUFPLEtBQUssSUFBSSxNQUFNLFNBQVMsQ0FBQztBQUNoQyxPQUFPLFlBQVksTUFBTSxlQUFlLENBQUM7QUFDekMsT0FBTyxFQUFDLEdBQUcsRUFBQyxNQUFNLFVBQVUsQ0FBQztBQUM3QixPQUFPLEtBQUssWUFBWSxNQUFNLG9CQUFvQixDQUFDO0FBQ25ELE9BQU8sRUFBQyxLQUFLLEVBQUMsTUFBTSxZQUFZLENBQUM7QUFDakMsT0FBTyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBQzVCLE9BQU8sRUFBQyxTQUFTLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUN6QyxPQUFPLEtBQUssUUFBUSxNQUFNLGVBQWUsQ0FBQztBQUMxQyxPQUFPLEVBQUMsT0FBTyxFQUFDLE1BQU0sZUFBZSxDQUFDO0FBQ3RDLE9BQU8sRUFBQyxjQUFjLEVBQUMsTUFBTSxzQkFBc0IsQ0FBQztBQUNwRCxPQUFPLEVBQUMsU0FBUyxFQUFDLE1BQU0sZ0JBQWdCLENBQUM7QUFDekMsT0FBTyxFQUFDLGtCQUFrQixFQUFDLE1BQU0sMkJBQTJCLENBQUM7QUFHN0QsT0FBTyxFQUFDLElBQUksSUFBSSx1QkFBdUIsRUFBQyxNQUFNLDJCQUEyQixDQUFDO0FBRTFFLE1BQU0sYUFBYSxHQUFHLENBQUMsQ0FBQztBQUN4QixNQUFNLDhCQUE4QixHQUFHLENBQUMsUUFBUSxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsT0FBTyxDQUFVLENBQUM7QUFHekYsTUFBTSxPQUFPLE1BQU07SUFFUCxPQUFPLEdBQWEsRUFBRSxDQUFDO0lBQ3ZCLFdB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QixDQUFDLEVBQUUsT0FBTyxDQUFDO0lBQy9ELE1BQU0sQ0FBQyxVQUFVLEtBQUssU0FBUyxFQUFFLEtBQUssQ0FBQSwwREFBMEQsY0FBYyxzQkFBc0IsZ0JBQWdCLGtEQUFrRCxDQUFDLENBQUM7SUFDeE0sT0FBTyxVQUFVLENBQUM7QUFDdEIsQ0FBQztBQUVELFNBQVMsb0JBQW9CLENBQUUsR0FBUTtJQUNuQyxNQUFNLEVBQUMsZ0JBQWdCLEVBQUUsbUJBQW1CLEVBQUMsR0FBRyxHQUFHLENBQUM7SUFDcEQsT0FBTyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsSUFBSSxJQUFJLFFBQVEsQ0FBQztBQUMvRSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNoYWxrIGZyb20gXCJjaGFsa1wiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCBkZWVwRXh0ZW5kIGZyb20gXCJkZWVwLWV4dGVuZFwiO1xuaW1wb3J0IGZzIGZyb20gXCJmcy1leHRyYVwiO1xuaW1wb3J0ICogYXMgeWFtbCBmcm9tIFwianMteWFtbFwiO1xuaW1wb3J0IHByZXR0eUhydGltZSBmcm9tIFwicHJldHR5LWhydGltZVwiO1xuaW1wb3J0IHtKb2J9IGZyb20gXCIuL2pvYi5qc1wiO1xuaW1wb3J0ICogYXMgRGF0YUV4cGFuZGVyIGZyb20gXCIuL2RhdGEtZXhwYW5kZXIuanNcIjtcbmltcG9ydCB7VXRpbHN9IGZyb20gXCIuL3V0aWxzLmpzXCI7XG5pbXBvcnQgYXNzZXJ0IGZyb20gXCJhc3NlcnRcIjtcbmltcG9ydCB7VmFsaWRhdG9yfSBmcm9tIFwiLi92YWxpZGF0b3IuanNcIjtcbmltcG9ydCAqIGFzIHBhcmFsbGVsIGZyb20gXCIuL3BhcmFsbGVsLmpzXCI7XG5pbXBvcnQge0dpdERhdGF9IGZyb20gXCIuL2dpdC1kYXRhLmpzXCI7XG5pbXBvcnQge1BhcnNlckluY2x1ZGVzfSBmcm9tIFwiLi9wYXJzZXItaW5jbHVkZXMuanNcIjtcbmltcG9ydCB7UHJvZHVjZXJzfSBmcm9tIFwiLi9wcm9kdWNlcnMuanNcIjtcbmltcG9ydCB7VmFyaWFibGVzRnJvbUZpbGVzfSBmcm9tIFwiLi92YXJpYWJsZXMtZnJvbS1maWxlcy5qc1wiO1xuaW1wb3J0IHtBcmd2fSBmcm9tIFwiLi9hcmd2LmpzXCI7XG5pbXBvcnQge1dyaXRlU3RyZWFtc30gZnJvbSBcIi4vd3JpdGUtc3RyZWFtcy5qc1wiO1xuaW1wb3J0IHtpbml0IGFzIGluaXRQcmVkZWZpbmVkVmFyaWFibGVzfSBmcm9tIFwiLi9wcmVkZWZpbmVkLXZhcmlhYmxlcy5qc1wiO1xuXG5jb25zdCBNQVhfRlVOQ1RJT05TID0gMztcbmNvbnN0IElOQ0xVREVfSU5QVVRTX1NVUFBPUlRFRF9UWVBFUyA9IFtcInN0cmluZ1wiLCBcImJvb2xlYW5cIiwgXCJudW1iZXJcIiwgXCJhcnJheVwiXSBhcyBjb25zdDtcbmV4cG9ydCB0eXBlIElucHV0VHlwZSA9IHR5cGVvZiBJTkNMVURFX0lOUFVUU19TVVBQT1JURURfVFlQRVNbbnVtYmVyXTtcblxuZXhwb3J0IGNsYXNzIFBhcnNlciB7XG5cbiAgICBwcml2YXRlIF9zdGFnZXM6IHN0cmluZ1tdID0gW107XG4gICAgcHJpdmF0ZSBfZ2l0bGFiRGF0YTogYW55O1xuICAgIHByaXZhdGUgX2pvYk5hbWVQYWQ6IG51bWJlciB8IG51bGwgPSBudWxsO1xuXG4gICAgcmVhZG9ubHkgam9iczogSm9iW107XG4gICAgcmVhZG9ubHkgYXJndjogQXJndjtcbiAgICByZWFkb25seSB3cml0ZVN0cmVhbXM6IFdyaXRlU3RyZWFtcztcbiAgICByZWFkb25seSBwaXBlbGluZUlpZDogbnVtYmVyO1xuICAgIHJlYWRvbmx5IGV4cGFuZFZhcmlhYmxlczogYm9vbGVhbjtcblxuICAgIHByaXZhdGUgY29uc3RydWN0b3IgKGFyZ3Y6IEFyZ3YsIHdyaXRlU3RyZWFtczogV3JpdGVTdHJlYW1zLCBwaXBlbGluZUlpZDogbnVtYmVyLCBqb2JzOiBKb2JbXSwgZXhwYW5kVmFyaWFibGVzOiBib29sZWFuKSB7XG4gICAgICAgIHRoaXMuYXJndiA9IGFyZ3Y7XG4gICAgICAgIHRoaXMud3JpdGVTdHJlYW1zID0gd3JpdGVTdHJlYW1zO1xuICAgICAgICB0aGlzLnBpcGVsaW5lSWlkID0gcGlwZWxpbmVJaWQ7XG4gICAgICAgIHRoaXMuam9icyA9IGpvYnM7XG4gICAgICAgIHRoaXMuZXhwYW5kVmFyaWFibGVzID0gZXhwYW5kVmFyaWFibGVzO1xuICAgIH1cblxuICAgIGdldCBzdGFnZXMgKCk6IHJlYWRvbmx5IHN0cmluZ1tdIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3N0YWdlcztcbiAgICB9XG5cbiAgICBnZXQgZ2l0bGFiRGF0YSAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9naXRsYWJEYXRhO1xuICAgIH1cblxuICAgIGdldCBqb2JOYW1lUGFkICgpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5fam9iTmFtZVBhZCA/PyAwO1xuICAgIH1cblxuICAgIHN0YXRpYyBhc3luYyBjcmVhdGUgKGFyZ3Y6IEFyZ3YsIHdyaXRlU3RyZWFtczogV3JpdGVTdHJlYW1zLCBwaXBlbGluZUlpZDogbnVtYmVyLCBqb2JzOiBKb2JbXSwgZXhwYW5kVmFyaWFibGVzOiBib29sZWFuID0gdHJ1ZSkge1xuICAgICAgICBjb25zdCBwYXJzZXIgPSBuZXcgUGFyc2VyKGFyZ3YsIHdyaXRlU3RyZWFtcywgcGlwZWxpbmVJaWQsIGpvYnMsIGV4cGFuZFZhcmlhYmxlcyk7XG4gICAgICAgIGNvbnN0IHRpbWUgPSBwcm9jZXNzLmhydGltZSgpO1xuICAgICAgICBhd2FpdCBwYXJzZXIuaW5pdCgpO1xuICAgICAgICBjb25zdCB3YXJuaW5ncyA9IGF3YWl0IFZhbGlkYXRvci5ydW4ocGFyc2VyLmpvYnMsIHBhcnNlci5zdGFnZXMpO1xuXG4gICAgICAgIGZvciAoY29uc3Qgam9iIG9mIHBhcnNlci5qb2JzKSB7XG4gICAgICAgICAgICBpZiAoam9iLmFydGlmYWN0cyA9PT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIGpvYi5kZWxldGVBcnRpZmFjdHMoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHBhcnNpbmdUaW1lID0gcHJvY2Vzcy5ocnRpbWUodGltZSk7XG4gICAgICAgIGNvbnN0IHBhdGhUb0V4cGFuZGVkR2l0TGFiQ2kgPSBwYXRoLmpvaW4oYXJndi5jd2QsIGFyZ3Yuc3RhdGVEaXIsIFwiZXhwYW5kZWQtZ2l0bGFiLWNpLnltbFwiKTtcbiAgICAgICAgZnMubWtkaXJwU3luYyhwYXRoLmpvaW4oYXJndi5jd2QsIGFyZ3Yuc3RhdGVEaXIpKTtcbiAgICAgICAgZnMud3JpdGVGaWxlU3luYyhwYXRoVG9FeHBhbmRlZEdpdExhYkNpLCB5YW1sLmR1bXAocGFyc2VyLmdpdGxhYkRhdGEpKTtcbiAgICAgICAgaWYgKGFyZ3YuY2hpbGRQaXBlbGluZURlcHRoID09IDApIHdyaXRlU3RyZWFtcy5zdGRlcnIoY2hhbGtge2dyZXkgcGFyc2luZyBhbmQgZG93bmxvYWRzIGZpbmlzaGVkIGluICR7cHJldHR5SHJ0aW1lKHBhcnNpbmdUaW1lKX0ufVxcbmApO1xuXG4gICAgICAgIGZvciAoY29uc3Qgd2FybmluZyBvZiB3YXJuaW5ncykge1xuICAgICAgICAgICAgd3JpdGVTdHJlYW1zLnN0ZGVycihjaGFsa2B7eWVsbG93ICR7d2FybmluZ319XFxuYCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyAjIFNlY29uZCBsYXllciBvZiBjaGVjayBmb3IgZXJyb3JzIHRoYXQgYXJlIG5vdCBjYXVnaHQgaW4gVmFsaWRhdG9yLnJ1blxuICAgICAgICBpZiAocGFyc2VyLmFyZ3YuanNvblNjaGVtYVZhbGlkYXRpb24pIHtcbiAgICAgICAgICAgIGNvbnN0IHRpbWUgPSBwcm9jZXNzLmhydGltZSgpO1xuICAgICAgICAgICAgVmFsaWRhdG9yLmpzb25TY2hlbWFWYWxpZGF0aW9uKHtcbiAgICAgICAgICAgICAgICBwYXRoVG9FeHBhbmRlZEdpdExhYkNpLFxuICAgICAgICAgICAgICAgIGdpdExhYkNpQ29uZmlnOiBwYXJzZXIuZ2l0bGFiRGF0YSxcbiAgICAgICAgICAgICAgICBhcmd2LFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBpZiAoYXJndi5jaGlsZFBpcGVsaW5lRGVwdGggPT0gMCkgd3JpdGVTdHJlYW1zLnN0ZGVycihjaGFsa2B7Z3JleSBqc29uIHNjaGVtYSB2YWxpZGF0ZWQgaW4gJHtwcmV0dHlIcnRpbWUocHJvY2Vzcy5ocnRpbWUodGltZSkpfX1cXG5gKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcGFyc2VyO1xuICAgIH1cblxuICAgIGFzeW5jIGluaXQgKCkge1xuICAgICAgICBjb25zdCBhcmd2ID0gdGhpcy5hcmd2O1xuICAgICAgICBjb25zdCBjd2QgPSBhcmd2LmN3ZDtcbiAgICAgICAgY29uc3Qgc3RhdGVEaXIgPSBhcmd2LnN0YXRlRGlyO1xuICAgICAgICBjb25zdCB3cml0ZVN0cmVhbXMgPSB0aGlzLndyaXRlU3RyZWFtcztcbiAgICAgICAgY29uc3QgZmlsZSA9IGFyZ3YuZmlsZTtcbiAgICAgICAgY29uc3QgcGlwZWxpbmVJaWQgPSB0aGlzLnBpcGVsaW5lSWlkO1xuICAgICAgICBjb25zdCBmZXRjaEluY2x1ZGVzID0gYXJndi5mZXRjaEluY2x1ZGVzO1xuICAgICAgICBjb25zdCBnaXREYXRhID0gYXdhaXQgR2l0RGF0YS5pbml0KGN3ZCwgd3JpdGVTdHJlYW1zKTtcbiAgICAgICAgY29uc3QgdmFyaWFibGVzRnJvbUZpbGVzID0gYXdhaXQgVmFyaWFibGVzRnJvbUZpbGVzLmluaXQoYXJndiwgd3JpdGVTdHJlYW1zLCBnaXREYXRhKTtcbiAgICAgICAgY29uc3QgZW52TWF0Y2hlZFZhcmlhYmxlcyA9IFV0aWxzLmZpbmRFbnZNYXRjaGVkVmFyaWFibGVzKHZhcmlhYmxlc0Zyb21GaWxlcyk7XG4gICAgICAgIGNvbnN0IHByZWRlZmluZWRWYXJpYWJsZXMgPSBpbml0UHJlZGVmaW5lZFZhcmlhYmxlcyh7Z2l0RGF0YSwgYXJndiwgZW52TWF0Y2hlZFZhcmlhYmxlc30pO1xuICAgICAgICBjb25zdCB2YXJpYWJsZXMgPSB7Li4ucHJlZGVmaW5lZFZhcmlhYmxlcywgLi4uZW52TWF0Y2hlZFZhcmlhYmxlcywgLi4uYXJndi52YXJpYWJsZX07XG4gICAgICAgIGNvbnN0IGV4cGFuZGVkID0gVXRpbHMuZXhwYW5kVmFyaWFibGVzKHZhcmlhYmxlcyk7XG5cbiAgICAgICAgbGV0IHlhbWxEYXRhTGlzdDogYW55W10gPSBbe3N0YWdlczogW1wiLnByZVwiLCBcImJ1aWxkXCIsIFwidGVzdFwiLCBcImRlcGxveVwiLCBcIi5wb3N0XCJdfV07XG4gICAgICAgIGNvbnN0IGdpdGxhYkNpRGF0YSA9IGF3YWl0IFBhcnNlci5sb2FkWWFtbChgJHtjd2R9LyR7ZmlsZX1gLCB7fSwgdGhpcy5leHBhbmRWYXJpYWJsZXMpO1xuXG4gICAgICAgIHlhbWxEYXRhTGlzdCA9IHlhbWxEYXRhTGlzdC5jb25jYXQoYXdhaXQgUGFyc2VySW5jbHVkZXMuaW5pdChnaXRsYWJDaURhdGEsIHthcmd2LCBjd2QsIHN0YXRlRGlyLCB3cml0ZVN0cmVhbXMsIGdpdERhdGEsIGZldGNoSW5jbHVkZXMsIHZhcmlhYmxlczogZXhwYW5kZWQsIGV4cGFuZFZhcmlhYmxlczogdGhpcy5leHBhbmRWYXJpYWJsZXMsIG1heGltdW1JbmNsdWRlczogYXJndi5tYXhpbXVtSW5jbHVkZXN9KSk7XG4gICAgICAgIFBhcnNlckluY2x1ZGVzLnJlc2V0Q291bnQoKTtcblxuICAgICAgICBjb25zdCBnaXRsYWJDaUxvY2FsRGF0YSA9IGF3YWl0IFBhcnNlci5sb2FkWWFtbChgJHtjd2R9Ly5naXRsYWItY2ktbG9jYWwueW1sYCwge30sIHRoaXMuZXhwYW5kVmFyaWFibGVzKTtcbiAgICAgICAgeWFtbERhdGFMaXN0ID0geWFtbERhdGFMaXN0LmNvbmNhdChhd2FpdCBQYXJzZXJJbmNsdWRlcy5pbml0KGdpdGxhYkNpTG9jYWxEYXRhLCB7YXJndiwgY3dkLCBzdGF0ZURpciwgd3JpdGVTdHJlYW1zLCBnaXREYXRhLCBmZXRjaEluY2x1ZGVzLCB2YXJpYWJsZXM6IGV4cGFuZGVkLCBleHBhbmRWYXJpYWJsZXM6IHRoaXMuZXhwYW5kVmFyaWFibGVzLCBtYXhpbXVtSW5jbHVkZXM6IGFyZ3YubWF4aW11bUluY2x1ZGVzfSkpO1xuICAgICAgICBQYXJzZXJJbmNsdWRlcy5yZXNldENvdW50KCk7XG5cbiAgICAgICAgY29uc3QgZ2l0bGFiRGF0YTogYW55ID0gZGVlcEV4dGVuZCh7fSwgLi4ueWFtbERhdGFMaXN0KTtcblxuICAgICAgICAvLyBFeHBhbmQgdmFyaW91cyBmaWVsZHMgaW4gZ2l0bGFiRGF0YVxuICAgICAgICBEYXRhRXhwYW5kZXIuam9iRXh0ZW5kcyhnaXRsYWJEYXRhKTtcbiAgICAgICAgRGF0YUV4cGFuZGVyLnJlZmVyZW5jZShnaXRsYWJEYXRhLCBnaXRsYWJEYXRhKTtcbiAgICAgICAgRGF0YUV4cGFuZGVyLmZsYXR0ZW5MaXN0cyhnaXRsYWJEYXRhKTtcbiAgICAgICAgRGF0YUV4cGFuZGVyLnRyYW5zZm9ybURlcHJlY2F0ZWRHbG9iYWxEZWZhdWx0U3ludGF4KGdpdGxhYkRhdGEpO1xuICAgICAgICBEYXRhRXhwYW5kZXIuaW5oZXJpdERlZmF1bHQoZ2l0bGFiRGF0YSk7XG4gICAgICAgIERhdGFFeHBhbmRlci5ub3JtYWxpemUoZ2l0bGFiRGF0YSk7XG5cbiAgICAgICAgYXNzZXJ0KGdpdGxhYkRhdGEuc3RhZ2VzICYmIEFycmF5LmlzQXJyYXkoZ2l0bGFiRGF0YS5zdGFnZXMpLCBjaGFsa2B7eWVsbG93IHN0YWdlczp9IG11c3QgYmUgYW4gYXJyYXlgKTtcbiAgICAgICAgaWYgKCFnaXRsYWJEYXRhLnN0YWdlcy5pbmNsdWRlcyhcIi5wcmVcIikpIHtcbiAgICAgICAgICAgIGdpdGxhYkRhdGEuc3RhZ2VzLnVuc2hpZnQoXCIucHJlXCIpO1xuICAgICAgICB9XG4gICAgICAgIGlmICghZ2l0bGFiRGF0YS5zdGFnZXMuaW5jbHVkZXMoXCIucG9zdFwiKSkge1xuICAgICAgICAgICAgZ2l0bGFiRGF0YS5zdGFnZXMucHVzaChcIi5wb3N0XCIpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuX3N0YWdlcyA9IGdpdGxhYkRhdGEuc3RhZ2VzO1xuXG4gICAgICAgIC8vIENoZWNrIGpvYiB2YXJpYWJsZXMgZm9yIGludmFsaWQgaGFzaCBvZiBrZXkgdmFsdWUgcGFpcnMsIGFuZCBjYXN0IG51bWJlcnMgdG8gc3RyaW5nc1xuICAgICAgICBVdGlscy5mb3JFYWNoUmVhbEpvYihnaXRsYWJEYXRhLCAoam9iTmFtZSwgam9iRGF0YSkgPT4ge1xuICAgICAgICAgICAgYXNzZXJ0KGpvYkRhdGEud2hlbiAhPT0gXCJuZXZlclwiLFxuICAgICAgICAgICAgICAgIGNoYWxrYFRoaXMgR2l0TGFiIENJIGNvbmZpZ3VyYXRpb24gaXMgaW52YWxpZDogam9iczoke2pvYk5hbWV9IHdoZW46bmV2ZXIgY2FuIG9ubHkgYmUgdXNlZCBpbiBhIHJ1bGVzIHNlY3Rpb24gb3Igd29ya2Zsb3c6cnVsZXNgLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGpvYkRhdGEudmFyaWFibGVzID8/IHt9KSkge1xuICAgICAgICAgICAgICAgIGpvYkRhdGEudmFyaWFibGVzW2tleV0gPSBVdGlscy5ub3JtYWxpemVWYXJpYWJsZXModmFsdWUpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IChqb2JEYXRhLnNlcnZpY2VzID8/IFtdKS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgIGNvbnN0IHNlcnZpY2UgPSBqb2JEYXRhLnNlcnZpY2VzW2ldO1xuICAgICAgICAgICAgICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHNlcnZpY2UudmFyaWFibGVzIHx8IHt9KSkge1xuICAgICAgICAgICAgICAgICAgICBhc3NlcnQoXG4gICAgICAgICAgICAgICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgfHwgdHlwZW9mIHZhbHVlID09PSBcIm51bWJlclwiIHx8IHR5cGVvZiB2YWx1ZSA9PT0gXCJib29sZWFuXCIsXG4gICAgICAgICAgICAgICAgICAgICAgICBjaGFsa2B7Ymx1ZUJyaWdodCAke2pvYk5hbWV9LnNlcnZpY2VzWyR7aX1dfSBoYXMgaW52YWxpZCB2YXJpYWJsZXMgaGFzaCBvZiBrZXkgdmFsdWUgcGFpcnMuICR7a2V5fT0ke3ZhbHVlfWAsXG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgIGpvYkRhdGEuc2VydmljZXNbaV0udmFyaWFibGVzW2tleV0gPSBTdHJpbmcodmFsdWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5fZ2l0bGFiRGF0YSA9IGdpdGxhYkRhdGE7XG5cbiAgICAgICAgLy8gR2VuZXJhdGUgam9icyBhbmQgcHV0IHRoZW0gaW50byBzdGFnZXNcbiAgICAgICAgVXRpbHMuZm9yRWFjaFJlYWxKb2IoZ2l0bGFiRGF0YSwgKGpvYk5hbWUsIGpvYkRhdGEpID0+IHtcbiAgICAgICAgICAgIGFzc2VydChnaXREYXRhICE9IG51bGwsIFwiZ2l0RGF0YSBtdXN0IGJlIHNldFwiKTtcbiAgICAgICAgICAgIGFzc2VydCh2YXJpYWJsZXNGcm9tRmlsZXMgIT0gbnVsbCwgXCJob21lVmFyaWFibGVzIG11c3QgYmUgc2V0XCIpO1xuXG4gICAgICAgICAgICBsZXQgbm9kZUluZGV4ID0gMTtcbiAgICAgICAgICAgIGNvbnN0IHBhcmFsbGVsTWF0cml4VmFyaWFibGVzTGlzdCA9IHBhcmFsbGVsLm1hdHJpeFZhcmlhYmx