gitlab-ci-local
Version:
Tired of pushing to test your .gitlab-ci.yml?
151 lines (150 loc) • 26 kB
JavaScript
import Ajv from "ajv";
import assert from "assert";
import chalk from "chalk";
import schema from "./schema/index.js";
import { betterAjvErrors } from "./schema-error.js";
import terminalLink from "terminal-link";
const MAX_ERRORS = 5;
export class Validator {
static jsonSchemaValidation({ pathToExpandedGitLabCi, gitLabCiConfig, argv }) {
const ajv = new Ajv({
verbose: true,
allErrors: true,
allowUnionTypes: true,
validateFormats: false,
strictTypes: false, // to suppress the missing types defined in the gitlab-ci json schema
keywords: ["markdownDescription"],
});
const validate = ajv.compile(schema);
const valid = validate(gitLabCiConfig);
if (valid)
return;
const betterErrors = betterAjvErrors({
data: gitLabCiConfig,
errors: validate.errors,
}).filter(betterError => !argv.ignoreSchemaPaths.includes(betterError.schemaPath));
let e = "";
for (let i = 0, len = betterErrors.length; i < len; i++) {
if (i + 1 > MAX_ERRORS) {
e += `\t... and ${len - MAX_ERRORS} more`;
break;
}
e += chalk `\t• {redBright ${betterErrors[i].message}} at {blueBright ${betterErrors[i].path}} {grey [${betterErrors[i].schemaPath}]}\n`;
}
assert(valid || betterErrors.length == 0, chalk `
{reset Invalid .gitlab-ci.yml configuration!
${e.trimEnd()}
For further troubleshooting, consider either of the following:
\t• Copy the content of {blueBright ${terminalLink(".gitlab-ci-local/expanded-gitlab-ci.yml", pathToExpandedGitLabCi)}} to the ${terminalLink("pipeline editor", "https://docs.gitlab.com/ee/ci/pipeline_editor/")} to debug it
\t• Use --ignore-schema-paths "#/definitions/tags/minItems" --ignore-schema-paths "#/additionalProperties" to partially disable certain validation rule
\t• Use --json-schema-validation=false to disable schema validation (not recommended)}
`);
}
static needs(jobs, stages) {
const warnings = [];
for (const job of jobs) {
if (job.needs === null || job.needs.length === 0)
continue;
for (const [i, need] of job.needs.entries()) {
if (need.pipeline) {
warnings.push(`${job.name}.needs[${i}].job:${need.job} ignored, pipeline key not supported`);
continue;
}
if (need.project) {
warnings.push(`${job.name}.needs[${i}] ignored, project key not supported`);
continue;
}
const needJob = jobs.find(j => j.baseName === need.job);
if (need.optional && !needJob)
continue;
assert(needJob != null, chalk `needs: [{blueBright ${need.job}}] for {blueBright ${job.baseName}} could not be found`);
const needJobStageIndex = stages.indexOf(needJob.stage);
const jobStageIndex = stages.indexOf(job.stage);
assert(needJobStageIndex <= jobStageIndex, chalk `needs: [{blueBright ${needJob.name}}] for {blueBright ${job.name}} is in a future stage`);
}
}
return warnings;
}
static dependencies(jobs, stages) {
for (const job of jobs) {
if (job.dependencies === null || job.dependencies.length === 0)
continue;
const undefDeps = job.dependencies.filter((j) => !jobs.some(n => n.baseName === j));
assert(undefDeps.length !== job.dependencies.length, chalk `dependencies: [{blueBright ${undefDeps.join(",")}}] for {blueBright ${job.name}} cannot be found`);
for (const dep of job.dependencies) {
const depJob = jobs.find(j => j.baseName === dep);
assert(depJob != null, chalk `dependencies: [{blueBright ${dep}}] for {blueBright ${job.baseName}} could not be found`);
const depJobStageIndex = stages.indexOf(depJob.stage);
const jobStageIndex = stages.indexOf(job.stage);
assert(depJobStageIndex <= jobStageIndex, chalk `dependencies: [{blueBright ${depJob.name}}] for {blueBright ${job.name}} is in a future stage`);
}
}
}
static dependenciesContainment(jobs) {
for (const job of jobs) {
const needs = job.needs;
const dependencies = job.dependencies;
if (needs && needs.length === 0)
continue;
if (!dependencies || !needs)
continue;
const everyIncluded = dependencies.every((dep) => {
return needs.some(n => n.job === dep);
});
const assertMsg = `${job.formattedJobName} needs: '${needs.map(n => n.job).join(",")}' doesn't fully contain dependencies: '${dependencies.join(",")}'`;
assert(everyIncluded, assertMsg);
}
}
/**
* These jobs named are reserved keywords in GitLab CI but does not prevent the pipeline from running
* https://github.com/firecow/gitlab-ci-local/issues/1263
* @param jobsNames
* @private
*/
static potentialIllegalJobName(jobsNames) {
const warnings = [];
for (const jobName of jobsNames) {
if (new Set(["types", "true", "false", "nil"]).has(jobName)) {
warnings.push(`Job name "${jobName}" is a reserved keyword. (https://docs.gitlab.com/ee/ci/jobs/#job-name-limitations)`);
}
}
return warnings;
}
static scriptBlank(jobs) {
for (const job of jobs) {
if (job.trigger)
continue; // Jobs with trigger are allowed to have empty script
assert(job.scripts.length > 0, chalk `{blue ${job.name}} has empty script`);
}
}
static arrayOfStrings(jobs) {
for (const job of jobs) {
if (job.trigger)
continue;
job.beforeScripts.forEach((s) => assert(typeof s === "string", chalk `{blue ${job.name}} before_script contains non string value`));
job.afterScripts.forEach((s) => assert(typeof s === "string", chalk `{blue ${job.name}} after_script contains non string value`));
job.scripts.forEach((s) => assert(typeof s === "string", chalk `{blue ${job.name}} script contains non string value`));
}
}
static async run(jobs, stages) {
const warnings = [];
this.scriptBlank(jobs);
this.arrayOfStrings(jobs);
warnings.push(...this.needs(jobs, stages));
this.dependencies(jobs, stages);
this.dependenciesContainment(jobs);
warnings.push(...this.potentialIllegalJobName(jobs.map(j => j.baseName)));
warnings.push(...this.artifacts(jobs));
return warnings;
}
static artifacts(jobs) {
const warnings = [];
for (const job of jobs) {
if (job.artifacts === null) {
warnings.push(`${job.name}.artifacts is null, ignoring.`);
}
}
return warnings;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsidmFsaWRhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sR0FBRyxNQUFNLEtBQUssQ0FBQztBQUV0QixPQUFPLE1BQU0sTUFBTSxRQUFRLENBQUM7QUFDNUIsT0FBTyxLQUFLLE1BQU0sT0FBTyxDQUFDO0FBQzFCLE9BQU8sTUFBTSxNQUFNLG1CQUFtQixDQUFDO0FBQ3ZDLE9BQU8sRUFBQyxlQUFlLEVBQUMsTUFBTSxtQkFBbUIsQ0FBQztBQUNsRCxPQUFPLFlBQVksTUFBTSxlQUFlLENBQUM7QUFHekMsTUFBTSxVQUFVLEdBQUcsQ0FBQyxDQUFDO0FBRXJCLE1BQU0sT0FBTyxTQUFTO0lBQ2xCLE1BQU0sQ0FBQyxvQkFBb0IsQ0FBRSxFQUFDLHNCQUFzQixFQUFFLGNBQWMsRUFBRSxJQUFJLEVBSXpFO1FBQ0csTUFBTSxHQUFHLEdBQUcsSUFBSSxHQUFHLENBQUM7WUFDaEIsT0FBTyxFQUFFLElBQUk7WUFDYixTQUFTLEVBQUUsSUFBSTtZQUNmLGVBQWUsRUFBRSxJQUFJO1lBQ3JCLGVBQWUsRUFBRSxLQUFLO1lBQ3RCLFdBQVcsRUFBRSxLQUFLLEVBQUUscUVBQXFFO1lBQ3pGLFFBQVEsRUFBRSxDQUFDLHFCQUFxQixDQUFDO1NBQ3BDLENBQUMsQ0FBQztRQUNILE1BQU0sUUFBUSxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDckMsTUFBTSxLQUFLLEdBQUcsUUFBUSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQ3ZDLElBQUksS0FBSztZQUFFLE9BQU87UUFDbEIsTUFBTSxZQUFZLEdBQUcsZUFBZSxDQUFDO1lBQ2pDLElBQUksRUFBRSxjQUFjO1lBQ3BCLE1BQU0sRUFBRSxRQUFRLENBQUMsTUFBTTtTQUMxQixDQUFDLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMs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QztZQUM5RCxDQUFDO1FBQ0wsQ0FBQztRQUNELE9BQU8sUUFBUSxDQUFDO0lBQ3BCLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBBanYgZnJvbSBcImFqdlwiO1xuaW1wb3J0IHtKb2J9IGZyb20gXCIuL2pvYi5qc1wiO1xuaW1wb3J0IGFzc2VydCBmcm9tIFwiYXNzZXJ0XCI7XG5pbXBvcnQgY2hhbGsgZnJvbSBcImNoYWxrXCI7XG5pbXBvcnQgc2NoZW1hIGZyb20gXCIuL3NjaGVtYS9pbmRleC5qc1wiO1xuaW1wb3J0IHtiZXR0ZXJBanZFcnJvcnN9IGZyb20gXCIuL3NjaGVtYS1lcnJvci5qc1wiO1xuaW1wb3J0IHRlcm1pbmFsTGluayBmcm9tIFwidGVybWluYWwtbGlua1wiO1xuaW1wb3J0IHtBcmd2fSBmcm9tIFwiLi9hcmd2LmpzXCI7XG5cbmNvbnN0IE1BWF9FUlJPUlMgPSA1O1xuXG5leHBvcnQgY2xhc3MgVmFsaWRhdG9yIHtcbiAgICBzdGF0aWMganNvblNjaGVtYVZhbGlkYXRpb24gKHtwYXRoVG9FeHBhbmRlZEdpdExhYkNpLCBnaXRMYWJDaUNvbmZpZywgYXJndn06IHtcbiAgICAgICAgcGF0aFRvRXhwYW5kZWRHaXRMYWJDaTogc3RyaW5nO1xuICAgICAgICBnaXRMYWJDaUNvbmZpZzogb2JqZWN0O1xuICAgICAgICBhcmd2OiBBcmd2O1xuICAgIH0pIHtcbiAgICAgICAgY29uc3QgYWp2ID0gbmV3IEFqdih7XG4gICAgICAgICAgICB2ZXJib3NlOiB0cnVlLFxuICAgICAgICAgICAgYWxsRXJyb3JzOiB0cnVlLFxuICAgICAgICAgICAgYWxsb3dVbmlvblR5cGVzOiB0cnVlLFxuICAgICAgICAgICAgdmFsaWRhdGVGb3JtYXRzOiBmYWxzZSxcbiAgICAgICAgICAgIHN0cmljdFR5cGVzOiBmYWxzZSwgLy8gdG8gc3VwcHJlc3MgdGhlIG1pc3NpbmcgdHlwZXMgZGVmaW5lZCBpbiB0aGUgZ2l0bGFiLWNpIGpzb24gc2NoZW1hXG4gICAgICAgICAgICBrZXl3b3JkczogW1wibWFya2Rvd25EZXNjcmlwdGlvblwiXSxcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0IHZhbGlkYXRlID0gYWp2LmNvbXBpbGUoc2NoZW1hKTtcbiAgICAgICAgY29uc3QgdmFsaWQgPSB2YWxpZGF0ZShnaXRMYWJDaUNvbmZpZyk7XG4gICAgICAgIGlmICh2YWxpZCkgcmV0dXJuO1xuICAgICAgICBjb25zdCBiZXR0ZXJFcnJvcnMgPSBiZXR0ZXJBanZFcnJvcnMoe1xuICAgICAgICAgICAgZGF0YTogZ2l0TGFiQ2lDb25maWcsXG4gICAgICAgICAgICBlcnJvcnM6IHZhbGlkYXRlLmVycm9ycyxcbiAgICAgICAgfSkuZmlsdGVyKGJldHRlckVycm9yID0+ICFhcmd2Lmlnbm9yZVNjaGVtYVBhdGhzLmluY2x1ZGVzKGJldHRlckVycm9yLnNjaGVtYVBhdGgpKTtcblxuICAgICAgICBsZXQgZTogc3RyaW5nID0gXCJcIjtcbiAgICAgICAgZm9yIChsZXQgaSA9IDAsIGxlbiA9IGJldHRlckVycm9ycy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuICAgICAgICAgICAgaWYgKGkgKyAxID4gTUFYX0VSUk9SUykge1xuICAgICAgICAgICAgICAgIGUgKz0gYFxcdC4uLiBhbmQgJHtsZW4gLSBNQVhfRVJST1JTfSBtb3JlYDtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGUgKz0gY2hhbGtgXFx04oCiIHtyZWRCcmlnaHQgJHtiZXR0ZXJFcnJvcnNbaV0ubWVzc2FnZX19IGF0IHtibHVlQnJpZ2h0ICR7YmV0dGVyRXJyb3JzW2ldLnBhdGh9fSB7Z3JleSBbJHtiZXR0ZXJFcnJvcnNbaV0uc2NoZW1hUGF0aH1dfVxcbmA7XG4gICAgICAgIH1cblxuICAgICAgICBhc3NlcnQodmFsaWQgfHwgYmV0dGVyRXJyb3JzLmxlbmd0aCA9PSAwLCBjaGFsa2BcbntyZXNldCBJbnZhbGlkIC5naXRsYWItY2kueW1sIGNvbmZpZ3VyYXRpb24hXG4ke2UudHJpbUVuZCgpfVxuXG5Gb3IgZnVydGhlciB0cm91Ymxlc2hvb3RpbmcsIGNvbnNpZGVyIGVpdGhlciBvZiB0aGUgZm9sbG93aW5nOlxuXFx04oCiIENvcHkgdGhlIGNvbnRlbnQgb2Yge2JsdWVCcmlnaHQgJHt0ZXJtaW5hbExpbmsoXCIuZ2l0bGFiLWNpLWxvY2FsL2V4cGFuZGVkLWdpdGxhYi1jaS55bWxcIiwgcGF0aFRvRXhwYW5kZWRHaXRMYWJDaSl9fSB0byB0aGUgJHt0ZXJtaW5hbExpbmsoXCJwaXBlbGluZSBlZGl0b3JcIiwgXCJodHRwczovL2RvY3MuZ2l0bGFiLmNvbS9lZS9jaS9waXBlbGluZV9lZGl0b3IvXCIpfSB0byBkZWJ1ZyBpdFxuXFx04oCiIFVzZSAtLWlnbm9yZS1zY2hlbWEtcGF0aHMgXCIjL2RlZmluaXRpb25zL3RhZ3MvbWluSXRlbXNcIiAtLWlnbm9yZS1zY2hlbWEtcGF0aHMgXCIjL2FkZGl0aW9uYWxQcm9wZXJ0aWVzXCIgdG8gcGFydGlhbGx5IGRpc2FibGUgY2VydGFpbiB2YWxpZGF0aW9uIHJ1bGVcblxcdOKAoiBVc2UgLS1qc29uLXNjaGVtYS12YWxpZGF0aW9uPWZhbHNlIHRvIGRpc2FibGUgc2NoZW1hIHZhbGlkYXRpb24gKG5vdCByZWNvbW1lbmRlZCl9XG5gKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHN0YXRpYyBuZWVkcyAoam9iczogUmVhZG9ubHlBcnJheTxKb2I+LCBzdGFnZXM6IHJlYWRvbmx5IHN0cmluZ1tdKTogc3RyaW5nW10ge1xuICAgICAgICBjb25zdCB3YXJuaW5nczogc3RyaW5nW10gPSBbXTtcbiAgICAgICAgZm9yIChjb25zdCBqb2Igb2Ygam9icykge1xuICAgICAgICAgICAgaWYgKGpvYi5uZWVkcyA9PT0gbnVsbCB8fCBqb2IubmVlZHMubGVuZ3RoID09PSAwKSBjb250aW51ZTtcblxuICAgICAgICAgICAgZm9yIChjb25zdCBbaSwgbmVlZF0gb2Ygam9iLm5lZWRzLmVudHJpZXMoKSkge1xuICAgICAgICAgICAgICAgIGlmIChuZWVkLnBpcGVsaW5lKSB7XG4gICAgICAgICAgICAgICAgICAgIHdhcm5pbmdzLnB1c2goYCR7am9iLm5hbWV9Lm5lZWRzWyR7aX1dLmpvYjoke25lZWQuam9ifSBpZ25vcmVkLCBwaXBlbGluZSBrZXkgbm90IHN1cHBvcnRlZGApO1xuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKG5lZWQucHJvamVjdCkge1xuICAgICAgICAgICAgICAgICAgICB3YXJuaW5ncy5wdXNoKGAke2pvYi5uYW1lfS5uZWVkc1ske2l9XSBpZ25vcmVkLCBwcm9qZWN0IGtleSBub3Qgc3VwcG9ydGVkYCk7XG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjb25zdCBuZWVkSm9iID0gam9icy5maW5kKGogPT4gai5iYXNlTmFtZSA9PT0gbmVlZC5qb2IpO1xuICAgICAgICAgICAgICAgIGlmIChuZWVkLm9wdGlvbmFsICYmICFuZWVkSm9iKSBjb250aW51ZTtcbiAgICAgICAgICAgICAgICBhc3NlcnQobmVlZEpvYiAhPSBudWxsLCBjaGFsa2BuZWVkczogW3tibHVlQnJpZ2h0ICR7bmVlZC5qb2J9fV0gZm9yIHtibHVlQnJpZ2h0ICR7am9iLmJhc2VOYW1lfX0gY291bGQgbm90IGJlIGZvdW5kYCk7XG4gICAgICAgICAgICAgICAgY29uc3QgbmVlZEpvYlN0YWdlSW5kZXggPSBzdGFnZXMuaW5kZXhPZihuZWVkSm9iLnN0YWdlKTtcbiAgICAgICAgICAgICAgICBjb25zdCBqb2JTdGFnZUluZGV4ID0gc3RhZ2VzLmluZGV4T2Yoam9iLnN0YWdlKTtcbiAgICAgICAgICAgICAgICBhc3NlcnQobmVlZEpvYlN0YWdlSW5kZXggPD0gam9iU3RhZ2VJbmRleCwgY2hhbGtgbmVlZHM6IFt7Ymx1ZUJyaWdodCAke25lZWRKb2IubmFtZX19XSBmb3Ige2JsdWVCcmlnaHQgJHtqb2IubmFtZX19IGlzIGluIGEgZnV0dXJlIHN0YWdlYCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gd2FybmluZ3M7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzdGF0aWMgZGVwZW5kZW5jaWVzIChqb2JzOiBSZWFkb25seUFycmF5PEpvYj4sIHN0YWdlczogcmVhZG9ubHkgc3RyaW5nW10pIHtcbiAgICAgICAgZm9yIChjb25zdCBqb2Igb2Ygam9icykge1xuICAgICAgICAgICAgaWYgKGpvYi5kZXBlbmRlbmNpZXMgPT09IG51bGwgfHwgam9iLmRlcGVuZGVuY2llcy5sZW5ndGggPT09IDApIGNvbnRpbnVlO1xuXG4gICAgICAgICAgICBjb25zdCB1bmRlZkRlcHMgPSBqb2IuZGVwZW5kZW5jaWVzLmZpbHRlcigoaikgPT4gIWpvYnMuc29tZShuID0+IG4uYmFzZU5hbWUgPT09IGopKTtcbiAgICAgICAgICAgIGFzc2VydCh1bmRlZkRlcHMubGVuZ3RoICE9PSBqb2IuZGVwZW5kZW5jaWVzLmxlbmd0aCwgY2hhbGtgZGVwZW5kZW5jaWVzOiBbe2JsdWVCcmlnaHQgJHt1bmRlZkRlcHMuam9pbihcIixcIil9fV0gZm9yIHtibHVlQnJpZ2h0ICR7am9iLm5hbWV9fSBjYW5ub3QgYmUgZm91bmRgKTtcblxuICAgICAgICAgICAgZm9yIChjb25zdCBkZXAgb2Ygam9iLmRlcGVuZGVuY2llcykge1xuICAgICAgICAgICAgICAgIGNvbnN0IGRlcEpvYiA9IGpvYnMuZmluZChqID0+IGouYmFzZU5hbWUgPT09IGRlcCk7XG4gICAgICAgICAgICAgICAgYXNzZXJ0KGRlcEpvYiAhPSBudWxsLCBjaGFsa2BkZXBlbmRlbmNpZXM6IFt7Ymx1ZUJyaWdodCAke2RlcH19XSBmb3Ige2JsdWVCcmlnaHQgJHtqb2IuYmFzZU5hbWV9fSBjb3VsZCBub3QgYmUgZm91bmRgKTtcbiAgICAgICAgICAgICAgICBjb25zdCBkZXBKb2JTdGFnZUluZGV4ID0gc3RhZ2VzLmluZGV4T2YoZGVwSm9iLnN0YWdlKTtcbiAgICAgICAgICAgICAgICBjb25zdCBqb2JTdGFnZUluZGV4ID0gc3RhZ2VzLmluZGV4T2Yoam9iLnN0YWdlKTtcbiAgICAgICAgICAgICAgICBhc3NlcnQoZGVwSm9iU3RhZ2VJbmRleCA8PSBqb2JTdGFnZUluZGV4LCBjaGFsa2BkZXBlbmRlbmNpZXM6IFt7Ymx1ZUJyaWdodCAke2RlcEpvYi5uYW1lfX1dIGZvciB7Ymx1ZUJyaWdodCAke2pvYi5uYW1lfX0gaXMgaW4gYSBmdXR1cmUgc3RhZ2VgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgc3RhdGljIGRlcGVuZGVuY2llc0NvbnRhaW5tZW50IChqb2JzOiBSZWFkb25seUFycmF5PEpvYj4pIHtcbiAgICAgICAgZm9yIChjb25zdCBqb2Igb2Ygam9icykge1xuICAgICAgICAgICAgY29uc3QgbmVlZHMgPSBqb2IubmVlZHM7XG4gICAgICAgICAgICBjb25zdCBkZXBlbmRlbmNpZXMgPSBqb2IuZGVwZW5kZW5jaWVzO1xuICAgICAgICAgICAgaWYgKG5lZWRzICYmIG5lZWRzLmxlbmd0aCA9PT0gMCkgY29udGludWU7XG4gICAgICAgICAgICBpZiAoIWRlcGVuZGVuY2llcyB8fCAhbmVlZHMpIGNvbnRpbnVlO1xuXG5cbiAgICAgICAgICAgIGNvbnN0IGV2ZXJ5SW5jbHVkZWQgPSBkZXBlbmRlbmNpZXMuZXZlcnkoKGRlcDogc3RyaW5nKSA9PiB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIG5lZWRzLnNvbWUobiA9PiBuLmpvYiA9PT0gZGVwKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgY29uc3QgYXNzZXJ0TXNnID0gYCR7am9iLmZvcm1hdHRlZEpvYk5hbWV9IG5lZWRzOiAnJHtuZWVkcy5tYXAobiA9PiBuLmpvYikuam9pbihcIixcIil9JyBkb2Vzbid0IGZ1bGx5IGNvbnRhaW4gZGVwZW5kZW5jaWVzOiAnJHtkZXBlbmRlbmNpZXMuam9pbihcIixcIil9J2A7XG4gICAgICAgICAgICBhc3NlcnQoZXZlcnlJbmNsdWRlZCwgYXNzZXJ0TXNnKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFRoZXNlIGpvYnMgbmFtZWQgYXJlIHJlc2VydmVkIGtleXdvcmRzIGluIEdpdExhYiBDSSBidXQgZG9lcyBub3QgcHJldmVudCB0aGUgcGlwZWxpbmUgZnJvbSBydW5uaW5nXG4gICAgICogaHR0cHM6Ly9naXRodWIuY29tL2ZpcmVjb3cvZ2l0bGFiLWNpLWxvY2FsL2lzc3Vlcy8xMjYzXG4gICAgICogQHBhcmFtIGpvYnNOYW1lc1xuICAgICAqIEBwcml2YXRlXG4gICAgICovXG4gICAgcHJpdmF0ZSBzdGF0aWMgcG90ZW50aWFsSWxsZWdhbEpvYk5hbWUgKGpvYnNOYW1lczogc3RyaW5nW10pIHtcbiAgICAgICAgY29uc3Qgd2FybmluZ3MgPSBbXTtcbiAgICAgICAgZm9yIChjb25zdCBqb2JOYW1lIG9mIGpvYnNOYW1lcykge1xuICAgICAgICAgICAgaWYgKG5ldyBTZXQoW1widHlwZXNcIiwgXCJ0cnVlXCIsIFwiZmFsc2VcIiwgXCJuaWxcIl0pLmhhcyhqb2JOYW1lKSkge1xuICAgICAgICAgICAgICAgIHdhcm5pbmdzLnB1c2goYEpvYiBuYW1lIFwiJHtqb2JOYW1lfVwiIGlzIGEgcmVzZXJ2ZWQga2V5d29yZC4gKGh0dHBzOi8vZG9jcy5naXRsYWIuY29tL2VlL2NpL2pvYnMvI2pvYi1uYW1lLWxpbWl0YXRpb25zKWApO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB3YXJuaW5ncztcbiAgICB9XG5cbiAgICBwcml2YXRlIHN0YXRpYyBzY3JpcHRCbGFuayAoam9iczogUmVhZG9ubHlBcnJheTxKb2I+KSB7XG4gICAgICAgIGZvciAoY29uc3Qgam9iIG9mIGpvYnMpIHtcbiAgICAgICAgICAgIGlmIChqb2IudHJpZ2dlcikgY29udGludWU7IC8vIEpvYnMgd2l0aCB0cmlnZ2VyIGFyZSBhbGxvd2VkIHRvIGhhdmUgZW1wdHkgc2NyaXB0XG4gICAgICAgICAgICBhc3NlcnQoam9iLnNjcmlwdHMubGVuZ3RoID4gMCwgY2hhbGtge2JsdWUgJHtqb2IubmFtZX19IGhhcyBlbXB0eSBzY3JpcHRgKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgc3RhdGljIGFycmF5T2ZTdHJpbmdzIChqb2JzOiBSZWFkb25seUFycmF5PEpvYj4pIHtcbiAgICAgICAgZm9yIChjb25zdCBqb2Igb2Ygam9icykge1xuICAgICAgICAgICAgaWYgKGpvYi50cmlnZ2VyKSBjb250aW51ZTtcbiAgICAgICAgICAgIGpvYi5iZWZvcmVTY3JpcHRzLmZvckVhY2goKHM6IGFueSkgPT4gYXNzZXJ0KHR5cGVvZiBzID09PSBcInN0cmluZ1wiLCBjaGFsa2B7Ymx1ZSAke2pvYi5uYW1lfX0gYmVmb3JlX3NjcmlwdCBjb250YWlucyBub24gc3RyaW5nIHZhbHVlYCkpO1xuICAgICAgICAgICAgam9iLmFmdGVyU2NyaXB0cy5mb3JFYWNoKChzOiBhbnkpID0+IGFzc2VydCh0eXBlb2YgcyA9PT0gXCJzdHJpbmdcIiwgY2hhbGtge2JsdWUgJHtqb2IubmFtZX19IGFmdGVyX3NjcmlwdCBjb250YWlucyBub24gc3RyaW5nIHZhbHVlYCkpO1xuICAgICAgICAgICAgam9iLnNjcmlwdHMuZm9yRWFjaCgoczogYW55KSA9PiBhc3NlcnQodHlwZW9mIHMgPT09IFwic3RyaW5nXCIsIGNoYWxrYHtibHVlICR7am9iLm5hbWV9fSBzY3JpcHQgY29udGFpbnMgbm9uIHN0cmluZyB2YWx1ZWApKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHN0YXRpYyBhc3luYyBydW4gKGpvYnM6IFJlYWRvbmx5QXJyYXk8Sm9iPiwgc3RhZ2VzOiByZWFkb25seSBzdHJpbmdbXSkge1xuICAgICAgICBjb25zdCB3YXJuaW5nczogc3RyaW5nW10gPSBbXTtcbiAgICAgICAgdGhpcy5zY3JpcHRCbGFuayhqb2JzKTtcbiAgICAgICAgdGhpcy5hcnJheU9mU3RyaW5ncyhqb2JzKTtcbiAgICAgICAgd2FybmluZ3MucHVzaCguLi50aGlzLm5lZWRzKGpvYnMsIHN0YWdlcykpO1xuICAgICAgICB0aGlzLmRlcGVuZGVuY2llcyhqb2JzLCBzdGFnZXMpO1xuICAgICAgICB0aGlzLmRlcGVuZGVuY2llc0NvbnRhaW5tZW50KGpvYnMpO1xuICAgICAgICB3YXJuaW5ncy5wdXNoKC4uLnRoaXMucG90ZW50aWFsSWxsZWdhbEpvYk5hbWUoam9icy5tYXAoaiA9PiBqLmJhc2VOYW1lKSkpO1xuICAgICAgICB3YXJuaW5ncy5wdXNoKC4uLnRoaXMuYXJ0aWZhY3RzKGpvYnMpKTtcbiAgICAgICAgcmV0dXJuIHdhcm5pbmdzO1xuICAgIH1cblxuICAgIHByaXZhdGUgc3RhdGljIGFydGlmYWN0cyAoam9iczogUmVhZG9ubHlBcnJheTxKb2I+KSB7XG4gICAgICAgIGNvbnN0IHdhcm5pbmdzOiBzdHJpbmdbXSA9IFtdO1xuICAgICAgICBmb3IgKGNvbnN0IGpvYiBvZiBqb2JzKSB7XG4gICAgICAgICAgICBpZiAoam9iLmFydGlmYWN0cyA9PT0gbnVsbCkge1xuICAgICAgICAgICAgICAgIHdhcm5pbmdzLnB1c2goYCR7am9iLm5hbWV9LmFydGlmYWN0cyBpcyBudWxsLCBpZ25vcmluZy5gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gd2FybmluZ3M7XG4gICAgfVxufVxuIl19