@cloudsnorkel/cdk-github-runners
Version:
CDK construct to create GitHub Actions self-hosted runners. Creates ephemeral runners on demand. Easy to deploy and highly customizable.
217 lines • 31.7 kB
JavaScript
;
var _a, _b;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScheduledWarmRunner = exports.AlwaysOnWarmRunner = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const crypto = require("crypto");
const cdk = require("aws-cdk-lib");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const constructs_1 = require("constructs");
const cron_parser_1 = require("cron-parser");
function buildWarmRunner(scope, props, schedule, duration, createInitialFill) {
const registrationLevel = props.registrationLevel ?? 'repo';
if (registrationLevel === 'org' && props.repo) {
cdk.Annotations.of(scope).addError('Do not specify repo when registrationLevel is \'org\'');
}
if (registrationLevel === 'repo' && !props.repo) {
cdk.Annotations.of(scope).addError('repo is required when registrationLevel is \'repo\'');
}
const providerPath = props.provider.node.path;
if (!props.runners.providers.some(p => p.node.path === providerPath)) {
cdk.Annotations.of(scope).addError(`Provider ${providerPath} is not in the providers list of the GitHubRunners construct`);
}
const labels = props.provider.labels;
const repo = registrationLevel === 'repo' ? (props.repo ?? '') : '';
const configHash = crypto.createHash('sha256')
.update(JSON.stringify({ providerPath, providerLabels: labels, count: props.count, duration, owner: props.owner, repo }))
.digest('hex')
.slice(0, 16);
const fillPayload = {
action: 'fill',
providerPath,
providerLabels: labels,
count: props.count,
duration,
owner: props.owner,
repo,
configHash,
};
const { lambda: managerFn, queue } = props.runners._ensureWarmRunnerInfra();
props.runners._registerWarmConfigHash(configHash);
// Schedule to fill the warm pool (usually daily). Sends to SQS so we get stable messageId for idempotent fills.
new aws_cdk_lib_1.aws_events.Rule(scope, 'Schedule', {
schedule,
targets: [new aws_cdk_lib_1.aws_events_targets.SqsQueue(queue, {
message: aws_cdk_lib_1.aws_events.RuleTargetInput.fromObject(fillPayload),
})],
});
// Fill the warm pool immediately on deploy (AlwaysOnWarmRunner only).
// ScheduledWarmRunner does not get deployment-fill. First fill happens at the next schedule fire.
if (createInitialFill) {
new cdk.CustomResource(scope, 'Initial Fill', {
serviceToken: managerFn.functionArn,
resourceType: 'Custom::WarmRunnerFill',
properties: fillPayload,
});
}
return fillPayload;
}
/**
* Warm runners that run 24/7. Fills at midnight UTC and each runner stays alive for 24 hours.
*
* Runners will be provisioned using the specified provider and registered in the specified repository or organization.
*
* Registration level must match the one selected during setup.
*
* @see https://github.com/CloudSnorkel/cdk-github-runners/blob/main/SETUP_GITHUB.md
*
* ## Limitations
*
* - Jobs will still trigger provisioning of on-demand runners, even if a warm runner ends up being used.
* - You may briefly see more than `count` runners when changing config or at rotation.
* - To remove: set `count` to 0, deploy, wait for warm runners to stop, then remove and deploy again.
* If you don't follow this procedure, warm runners may linger until they expire.
* - Provider failures or timeouts (like Lambda provider timing out after 15 minutes) will result in a
* gap in coverage until the retry succeeds. Current retry mechanism has built-in back-off rate and
* can be tweaked using `retryOptions`. This will be improved in the future.
*
* ```typescript
* new AlwaysOnWarmRunner(stack, 'AlwaysOnLinux', {
* runners,
* provider: myProvider,
* count: 3,
* owner: 'my-org',
* repo: 'my-repo',
* });
* ```
*/
class AlwaysOnWarmRunner extends constructs_1.Construct {
constructor(scope, id, props) {
super(scope, id);
this._fillPayload = buildWarmRunner(this, props, aws_cdk_lib_1.aws_events.Schedule.cron({ hour: '0', minute: '0' }), cdk.Duration.days(1).toSeconds(), true);
}
}
exports.AlwaysOnWarmRunner = AlwaysOnWarmRunner;
_a = JSII_RTTI_SYMBOL_1;
AlwaysOnWarmRunner[_a] = { fqn: "@cloudsnorkel/cdk-github-runners.AlwaysOnWarmRunner", version: "0.15.1" };
/**
* Convert AWS EventBridge cron format to cron-parser format.
* AWS: cron(min hour dom month dow year), cron-parser: sec min hour dom month dow
*/
function awsCronToParserFormat(expressionString) {
const match = expressionString.match(/^cron\((.+)\)$/);
if (!match)
return expressionString;
const [, inner] = match;
const parts = inner.trim().split(/\s+/);
if (parts.length !== 6)
return expressionString;
const [minute, hour, dom, month, dow] = parts;
return `0 ${minute} ${hour} ${dom} ${month} ${dow}`;
}
/**
* Parse AWS EventBridge rate expression and return interval in seconds.
* Format: rate(value unit) e.g. rate(2 hours), rate(5 minutes), rate(1 day)
*/
function parseRateInterval(expressionString) {
const match = expressionString.match(/^rate\((\d+)\s+(minute|minutes|hour|hours|day|days)\)$/i);
if (!match)
return undefined;
const value = parseInt(match[1], 10);
const unit = match[2].toLowerCase();
if (value <= 0)
return undefined;
const secondsPerUnit = {
minute: 60,
minutes: 60,
hour: 3600,
hours: 3600,
day: 86400,
days: 86400,
};
return value * secondsPerUnit[unit];
}
/**
* Get the interval between schedule occurrences in seconds.
* Supports both cron and rate expressions.
*/
function getScheduleIntervalSeconds(expressionString) {
const rateInterval = parseRateInterval(expressionString);
if (rateInterval !== undefined)
return rateInterval;
try {
const cronExpression = cron_parser_1.CronExpressionParser.parse(awsCronToParserFormat(expressionString));
const next = cronExpression.take(2);
return (next[1].getTime() - next[0].getTime()) / 1000;
}
catch {
return undefined;
}
}
/**
* Warm runners active during a time window specified by start time (`schedule`) and duration (`duration`).
*
* Runners will be provisioned using the specified provider and registered in the specified repository or organization.
*
* Registration level must match the one selected during setup.
*
* @see https://github.com/CloudSnorkel/cdk-github-runners/blob/main/SETUP_GITHUB.md
*
* ## Limitations
*
* - **No deployment-fill**: Unlike `AlwaysOnWarmRunner`, scheduled warm runners do not get an initial
* fill on deploy. The first fill happens at the next schedule occurrence. If you deploy at 1pm for
* a 2pm schedule, runners will not appear until 2pm.
* - Jobs will still trigger provisioning of on-demand runners, even if a warm runner ends up being used.
* - You may briefly see more than `count` runners when changing config or at rotation.
* - To remove: set `count` to 0, deploy, wait for warm runners to stop, then remove and deploy again.
* If you don't follow this procedure, warm runners may linger until they expire.
* - Provider failures or timeouts (like Lambda provider timing out after 15 minutes) will result in a
* gap in coverage until the retry succeeds. Current retry mechanism has built-in back-off rate and
* can be tweaked using `retryOptions`. This will be improved in the future.
*
* ```typescript
* // Cron: fill at 1pm on weekdays
* new ScheduledWarmRunner(stack, 'Business Hours', {
* runners,
* provider: myProvider,
* count: 3,
* owner: 'my-org',
* repo: 'my-repo',
* schedule: events.Schedule.cron({ hour: '13', minute: '0', weekDay: 'MON-FRI' }),
* duration: cdk.Duration.hours(2),
* });
* ```
*
* ```typescript
* // Rate: fill every 12 hours
* new ScheduledWarmRunner(stack, 'Every 12 Hours', {
* runners,
* provider: myProvider,
* count: 2,
* owner: 'my-org',
* repo: 'my-repo',
* schedule: events.Schedule.rate(cdk.Duration.hours(5)),
* duration: cdk.Duration.hours(12),
* });
* ```
*/
class ScheduledWarmRunner extends constructs_1.Construct {
constructor(scope, id, props) {
super(scope, id);
// make sure the duration is not longer than the interval between next two schedule occurrences
const interval = getScheduleIntervalSeconds(props.schedule.expressionString);
if (interval !== undefined && interval < props.duration.toSeconds()) {
cdk.Annotations.of(this).addError(`ScheduledWarmRunner duration ${props.duration.toHumanString()} is longer than the interval ${cdk.Duration.seconds(interval).toHumanString()} between next two schedule occurrences. This will result in overlapping warm runners at the start of the next schedule occurrence.`);
}
// warn for short interval
if (interval !== undefined && interval < cdk.Duration.hours(1).toSeconds()) {
cdk.Annotations.of(this).addWarningV2('@cloudsnorkel/cdk-github-runners:ScheduledWarmRunner.intervalTooShort', `ScheduledWarmRunner interval ${cdk.Duration.seconds(interval).toHumanString()} is less than 1 hour, which may result in more warm runners than expected`);
}
this._fillPayload = buildWarmRunner(this, props, props.schedule, props.duration.toSeconds(), false);
}
}
exports.ScheduledWarmRunner = ScheduledWarmRunner;
_b = JSII_RTTI_SYMBOL_1;
ScheduledWarmRunner[_b] = { fqn: "@cloudsnorkel/cdk-github-runners.ScheduledWarmRunner", version: "0.15.1" };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2FybS1ydW5uZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvd2FybS1ydW5uZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxpQ0FBaUM7QUFDakMsbUNBQW1DO0FBQ25DLDZDQUdxQjtBQUNyQiwyQ0FBdUM7QUFDdkMsNkNBQW1EO0FBcUVuRCxTQUFTLGVBQWUsQ0FBQyxLQUFnQixFQUFFLEtBQTBCLEVBQUUsUUFBeUIsRUFBRSxRQUFnQixFQUFFLGlCQUEwQjtJQUM1SSxNQUFNLGlCQUFpQixHQUFHLEtBQUssQ0FBQyxpQkFBaUIsSUFBSSxNQUFNLENBQUM7SUFDNUQsSUFBSSxpQkFBaUIsS0FBSyxLQUFLLElBQUksS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzlDLEdBQUcsQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLFFBQVEsQ0FBQyx1REFBdUQsQ0FBQyxDQUFDO0lBQzlGLENBQUM7SUFDRCxJQUFJLGlCQUFpQixLQUFLLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNoRCxHQUFHLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxRQUFRLENBQUMscURBQXFELENBQUMsQ0FBQztJQUM1RixDQUFDO0lBRUQsTUFBTSxZQUFZLEdBQUcsS0FBSyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDO0lBQzlDLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksS0FBSyxZQUFZLENBQUMsRUFBRSxDQUFDO1FBQ3JFLEdBQUcsQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLFFBQVEsQ0FBQyxZQUFZLFlBQVksOERBQThELENBQUMsQ0FBQztJQUM3SCxDQUFDO0lBRUQsTUFB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QixJQUFJLFFBQVEsS0FBSyxTQUFTLElBQUksUUFBUSxHQUFHLEdBQUcsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsRUFBRSxFQUFFLENBQUM7WUFDM0UsR0FBRyxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsWUFBWSxDQUNuQyx1RUFBdUUsRUFDdkUsZ0NBQWdDLEdBQUcsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLGFBQWEsRUFBRSwyRUFBMkUsQ0FDMUosQ0FBQztRQUNKLENBQUM7UUFFRCxJQUFJLENBQUMsWUFBWSxHQUFHLGVBQWUsQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxRQUFRLEVBQUUsS0FBSyxDQUFDLFFBQVEsQ0FBQyxTQUFTLEVBQUUsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUN0RyxDQUFDOztBQXpCSCxrREEwQkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBjcnlwdG8gZnJvbSAnY3J5cHRvJztcbmltcG9ydCAqIGFzIGNkayBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQge1xuICBhd3NfZXZlbnRzIGFzIGV2ZW50cyxcbiAgYXdzX2V2ZW50c190YXJnZXRzIGFzIGV2ZW50c190YXJnZXRzLFxufSBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCB7IENyb25FeHByZXNzaW9uUGFyc2VyIH0gZnJvbSAnY3Jvbi1wYXJzZXInO1xuaW1wb3J0IHsgSUNvbXBvc2l0ZVByb3ZpZGVyLCBJUnVubmVyUHJvdmlkZXIgfSBmcm9tICcuL3Byb3ZpZGVycyc7XG5pbXBvcnQgeyBHaXRIdWJSdW5uZXJzIH0gZnJvbSAnLi9ydW5uZXInO1xuaW1wb3J0IHsgV2FybVJ1bm5lckZpbGxQYXlsb2FkIH0gZnJvbSAnLi93YXJtLXJ1bm5lci1tYW5hZ2VyLmxhbWJkYSc7XG5cbi8qKlxuICogQ29tbW9uIHByb3BlcnRpZXMgZm9yIHdhcm0gcnVubmVyIGNvbnN0cnVjdHMuXG4gKlxuICogQGludGVybmFsXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgV2FybVJ1bm5lckJhc2VQcm9wcyB7XG4gIC8qKlxuICAgKiBUaGUgR2l0SHViUnVubmVycyBjb25zdHJ1Y3QgdGhhdCBvd25zIHRoZSBzaGFyZWQgd2FybSBydW5uZXIgaW5mcmFzdHJ1Y3R1cmUuXG4gICAqL1xuICByZWFkb25seSBydW5uZXJzOiBHaXRIdWJSdW5uZXJzO1xuXG4gIC8qKlxuICAgKiBQcm92aWRlciB0byB1c2UuIFdhcm0gcnVubmVycyBieXBhc3MgdGhlIHByb3ZpZGVyIHNlbGVjdG9yIOKAlCB0aGV5IGFsd2F5cyB1c2VcbiAgICogdGhpcyBwcm92aWRlciwgcmVnYXJkbGVzcyBvZiBqb2IgY2hhcmFjdGVyaXN0aWNzLiBMYWJlbHMgY2Fubm90IGJlIG1vZGlmaWVkLlxuICAgKi9cbiAgcmVhZG9ubHkgcHJvdmlkZXI6IElSdW5uZXJQcm92aWRlciB8IElDb21wb3NpdGVQcm92aWRlcjtcblxuICAvKipcbiAgICogTnVtYmVyIG9mIHdhcm0gcnVubmVycyB0byBtYWludGFpbi5cbiAgICovXG4gIHJlYWRvbmx5IGNvdW50OiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIEdpdEh1YiBvd25lciB3aGVyZSBydW5uZXJzIHdpbGwgYmUgcmVnaXN0ZXJlZCAob3JnIG9yIHVzZXIgbG9naW4pLlxuICAgKi9cbiAgcmVhZG9ubHkgb3duZXI6IHN0cmluZztcblxuICAvKipcbiAgICogUmVnaXN0cmF0aW9uIGxldmVsIOKAlCBtdXN0IG1hdGNoIGhvdyB5b3VyIHJ1bm5lcnMgYXJlIHNldCB1cCBpbiBHaXRIdWIuIENob29zZVxuICAgKiAnb3JnJyBmb3Igb3JnLXdpZGUgcnVubmVycywgJ3JlcG8nIGZvciByZXBvLWxldmVsLiBTZWUgdGhlIHNldHVwIHdpemFyZCBmb3IgY2hvb3NpbmcgcmVwbyB2cyBvcmcuXG4gICAqXG4gICAqIEBzZWUgaHR0cHM6Ly9naXRodWIuY29tL0Nsb3VkU25vcmtlbC9jZGstZ2l0aHViLXJ1bm5lcnMvYmxvYi9tYWluL1NFVFVQX0dJVEhVQi5tZFxuICAgKlxuICAgKiBAZGVmYXVsdCAncmVwbydcbiAgICovXG4gIHJlYWRvbmx5IHJlZ2lzdHJhdGlvbkxldmVsPzogJ29yZycgfCAncmVwbyc7XG5cbiAgLyoqXG4gICAqIFJlcG9zaXRvcnkgbmFtZSAod2l0aG91dCBvd25lcikgd2hlcmUgcnVubmVycyB3aWxsIGJlIHJlZ2lzdGVyZWQuIFJlcXVpcmVkIHdoZW4gYHJlZ2lzdHJhdGlvbkxldmVsYCBpcyAncmVwbycuXG4gICAqL1xuICByZWFkb25seSByZXBvPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIFByb3BlcnRpZXMgZm9yIGFsd2F5cyBvbiB3YXJtIHJ1bm5lcnMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQWx3YXlzT25XYXJtUnVubmVyUHJvcHMgZXh0ZW5kcyBXYXJtUnVubmVyQmFzZVByb3BzIHsgfVxuXG4vKipcbiAqIFByb3BlcnRpZXMgZm9yIHNjaGVkdWxlZCB3YXJtIHJ1bm5lcnMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2NoZWR1bGVkV2FybVJ1bm5lclByb3BzIGV4dGVuZHMgV2FybVJ1bm5lckJhc2VQcm9wcyB7XG4gIC8qKlxuICAgKiBXaGVuIHRvIHN0YXJ0IGZpbGxpbmcgdGhlIHBvb2wgKGUuZy4gc3RhcnQgb2YgYnVzaW5lc3MgaG91cnMpLlxuICAgKi9cbiAgcmVhZG9ubHkgc2NoZWR1bGU6IGV2ZW50cy5TY2hlZHVsZTtcblxuICAvKipcbiAgICogSG93IGxvbmcgdGhlIHdhcm0gcnVubmVycyBzaG91bGQgYmUgbWFpbnRhaW5lZCBmcm9tIHRoZSBmaWxsIHRpbWUgKHNjaGVkdWxlKS4gRGVmaW5lcyB0aGUgZW5kIG9mIHRoZVxuICAgKiB3aW5kb3cgKHNjaGVkdWxlIHRpbWUgKyBkdXJhdGlvbikuXG4gICAqL1xuICByZWFkb25seSBkdXJhdGlvbjogY2RrLkR1cmF0aW9uO1xufVxuXG5mdW5jdGlvbiBidWlsZFdhcm1SdW5uZXIoc2NvcGU6IENvbnN0cnVjdCwgcHJvcHM6IFdhcm1SdW5uZXJCYXNlUHJvcHMsIHNjaGVkdWxlOiBldmVudHMuU2NoZWR1bGUsIGR1cmF0aW9uOiBudW1iZXIsIGNyZWF0ZUluaXRpYWxGaWxsOiBib29sZWFuKSB7XG4gIGNvbnN0IHJlZ2lzdHJhdGlvbkxldmVsID0gcHJvcHMucmVnaXN0cmF0aW9uTGV2ZWwgPz8gJ3JlcG8nO1xuICBpZiAocmVnaXN0cmF0aW9uTGV2ZWwgPT09ICdvcmcnICYmIHByb3BzLnJlcG8pIHtcbiAgICBjZGsuQW5ub3RhdGlvbnMub2Yoc2NvcGUpLmFkZEVycm9yKCdEbyBub3Qgc3BlY2lmeSByZXBvIHdoZW4gcmVnaXN0cmF0aW9uTGV2ZWwgaXMgXFwnb3JnXFwnJyk7XG4gIH1cbiAgaWYgKHJlZ2lzdHJhdGlvbkxldmVsID09PSAncmVwbycgJiYgIXByb3BzLnJlcG8pIHtcbiAgICBjZGsuQW5ub3RhdGlvbnMub2Yoc2NvcGUpLmFkZEVycm9yKCdyZXBvIGlzIHJlcXVpcmVkIHdoZW4gcmVnaXN0cmF0aW9uTGV2ZWwgaXMgXFwncmVwb1xcJycpO1xuICB9XG5cbiAgY29uc3QgcHJvdmlkZXJQYXRoID0gcHJvcHMucHJvdmlkZXIubm9kZS5wYXRoO1xuICBpZiAoIXByb3BzLnJ1bm5lcnMucHJvdmlkZXJzLnNvbWUocCA9PiBwLm5vZGUucGF0aCA9PT0gcHJvdmlkZXJQYXRoKSkge1xuICAgIGNkay5Bbm5vdGF0aW9ucy5vZihzY29wZSkuYWRkRXJyb3IoYFByb3ZpZGVyICR7cHJvdmlkZXJQYXRofSBpcyBub3QgaW4gdGhlIHByb3ZpZGVycyBsaXN0IG9mIHRoZSBHaXRIdWJSdW5uZXJzIGNvbnN0cnVjdGApO1xuICB9XG5cbiAgY29uc3QgbGFiZWxzID0gcHJvcHMucHJvdmlkZXIubGFiZWxzO1xuXG4gIGNvbnN0IHJlcG8gPSByZWdpc3RyYXRpb25MZXZlbCA9PT0gJ3JlcG8nID8gKHByb3BzLnJlcG8gPz8gJycpIDogJyc7XG4gIGNvbnN0IGNvbmZpZ0hhc2ggPSBjcnlwdG8uY3JlYXRlSGFzaCgnc2hhMjU2JylcbiAgICAudXBkYXRlKEpTT04uc3RyaW5naWZ5KHsgcHJvdmlkZXJQYXRoLCBwcm92aWRlckxhYmVsczogbGFiZWxzLCBjb3VudDogcHJvcHMuY291bnQsIGR1cmF0aW9uLCBvd25lcjogcHJvcHMub3duZXIsIHJlcG8gfSkpXG4gICAgLmRpZ2VzdCgnaGV4JylcbiAgICAuc2xpY2UoMCwgMTYpO1xuXG4gIGNvbnN0IGZpbGxQYXlsb2FkOiBXYXJtUnVubmVyRmlsbFBheWxvYWQgPSB7XG4gICAgYWN0aW9uOiAnZmlsbCcgYXMgY29uc3QsXG4gICAgcHJvdmlkZXJQYXRoLFxuICAgIHByb3ZpZGVyTGFiZWxzOiBsYWJlbHMsXG4gICAgY291bnQ6IHByb3BzLmNvdW50LFxuICAgIGR1cmF0aW9uLFxuICAgIG93bmVyOiBwcm9wcy5vd25lcixcbiAgICByZXBvLFxuICAgIGNvbmZpZ0hhc2gsXG4gIH07XG5cbiAgY29uc3QgeyBsYW1iZGE6IG1hbmFnZXJGbiwgcXVldWUgfSA9IHByb3BzLnJ1bm5lcnMuX2Vuc3VyZVdhcm1SdW5uZXJJbmZyYSgpO1xuICBwcm9wcy5ydW5uZXJzLl9yZWdpc3Rlcldhcm1Db25maWdIYXNoKGNvbmZpZ0hhc2gpO1xuXG4gIC8vIFNjaGVkdWxlIHRvIGZpbGwgdGhlIHdhcm0gcG9vbCAodXN1YWxseSBkYWlseSkuIFNlbmRzIHRvIFNRUyBzbyB3ZSBnZXQgc3RhYmxlIG1lc3NhZ2VJZCBmb3IgaWRlbXBvdGVudCBmaWxscy5cbiAgbmV3IGV2ZW50cy5SdWxlKHNjb3BlLCAnU2NoZWR1bGUnLCB7XG4gICAgc2NoZWR1bGUsXG4gICAgdGFyZ2V0czogW25ldyBldmVudHNfdGFyZ2V0cy5TcXNRdWV1ZShxdWV1ZSwge1xuICAgICAgbWVzc2FnZTogZXZlbnRzLlJ1bGVUYXJnZXRJbnB1dC5mcm9tT2JqZWN0KGZpbGxQYXlsb2FkKSxcbiAgICB9KV0sXG4gIH0pO1xuXG4gIC8vIEZpbGwgdGhlIHdhcm0gcG9vbCBpbW1lZGlhdGVseSBvbiBkZXBsb3kgKEFsd2F5c09uV2FybVJ1bm5lciBvbmx5KS5cbiAgLy8gU2NoZWR1bGVkV2FybVJ1bm5lciBkb2VzIG5vdCBnZXQgZGVwbG95bWVudC1maWxsLiBGaXJzdCBmaWxsIGhhcHBlbnMgYXQgdGhlIG5leHQgc2NoZWR1bGUgZmlyZS5cbiAgaWYgKGNyZWF0ZUluaXRpYWxGaWxsKSB7XG4gICAgbmV3IGNkay5DdXN0b21SZXNvdXJjZShzY29wZSwgJ0luaXRpYWwgRmlsbCcsIHtcbiAgICAgIHNlcnZpY2VUb2tlbjogbWFuYWdlckZuLmZ1bmN0aW9uQXJuLFxuICAgICAgcmVzb3VyY2VUeXBlOiAnQ3VzdG9tOjpXYXJtUnVubmVyRmlsbCcsXG4gICAgICBwcm9wZXJ0aWVzOiBmaWxsUGF5bG9hZCxcbiAgICB9KTtcbiAgfVxuXG4gIHJldHVybiBmaWxsUGF5bG9hZDtcbn1cblxuLyoqXG4gKiBXYXJtIHJ1bm5lcnMgdGhhdCBydW4gMjQvNy4gRmlsbHMgYXQgbWlkbmlnaHQgVVRDIGFuZCBlYWNoIHJ1bm5lciBzdGF5cyBhbGl2ZSBmb3IgMjQgaG91cnMuXG4gKlxuICogUnVubmVycyB3aWxsIGJlIHByb3Zpc2lvbmVkIHVzaW5nIHRoZSBzcGVjaWZpZWQgcHJvdmlkZXIgYW5kIHJlZ2lzdGVyZWQgaW4gdGhlIHNwZWNpZmllZCByZXBvc2l0b3J5IG9yIG9yZ2FuaXphdGlvbi5cbiAqXG4gKiBSZWdpc3RyYXRpb24gbGV2ZWwgbXVzdCBtYXRjaCB0aGUgb25lIHNlbGVjdGVkIGR1cmluZyBzZXR1cC5cbiAqXG4gKiBAc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9DbG91ZFNub3JrZWwvY2RrLWdpdGh1Yi1ydW5uZXJzL2Jsb2IvbWFpbi9TRVRVUF9HSVRIVUIubWRcbiAqXG4gKiAjIyBMaW1pdGF0aW9uc1xuICpcbiAqIC0gSm9icyB3aWxsIHN0aWxsIHRyaWdnZXIgcHJvdmlzaW9uaW5nIG9mIG9uLWRlbWFuZCBydW5uZXJzLCBldmVuIGlmIGEgd2FybSBydW5uZXIgZW5kcyB1cCBiZWluZyB1c2VkLlxuICogLSBZb3UgbWF5IGJyaWVmbHkgc2VlIG1vcmUgdGhhbiBgY291bnRgIHJ1bm5lcnMgd2hlbiBjaGFuZ2luZyBjb25maWcgb3IgYXQgcm90YXRpb24uXG4gKiAtIFRvIHJlbW92ZTogc2V0IGBjb3VudGAgdG8gMCwgZGVwbG95LCB3YWl0IGZvciB3YXJtIHJ1bm5lcnMgdG8gc3RvcCwgdGhlbiByZW1vdmUgYW5kIGRlcGxveSBhZ2Fpbi5cbiAqICAgSWYgeW91IGRvbid0IGZvbGxvdyB0aGlzIHByb2NlZHVyZSwgd2FybSBydW5uZXJzIG1heSBsaW5nZXIgdW50aWwgdGhleSBleHBpcmUuXG4gKiAtIFByb3ZpZGVyIGZhaWx1cmVzIG9yIHRpbWVvdXRzIChsaWtlIExhbWJkYSBwcm92aWRlciB0aW1pbmcgb3V0IGFmdGVyIDE1IG1pbnV0ZXMpIHdpbGwgcmVzdWx0IGluIGFcbiAqICAgZ2FwIGluIGNvdmVyYWdlIHVudGlsIHRoZSByZXRyeSBzdWNjZWVkcy4gQ3VycmVudCByZXRyeSBtZWNoYW5pc20gaGFzIGJ1aWx0LWluIGJhY2stb2ZmIHJhdGUgYW5kXG4gKiAgIGNhbiBiZSB0d2Vha2VkIHVzaW5nIGByZXRyeU9wdGlvbnNgLiBUaGlzIHdpbGwgYmUgaW1wcm92ZWQgaW4gdGhlIGZ1dHVyZS5cbiAqXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBuZXcgQWx3YXlzT25XYXJtUnVubmVyKHN0YWNrLCAnQWx3YXlzT25MaW51eCcsIHtcbiAqICAgcnVubmVycyxcbiAqICAgcHJvdmlkZXI6IG15UHJvdmlkZXIsXG4gKiAgIGNvdW50OiAzLFxuICogICBvd25lcjogJ215LW9yZycsXG4gKiAgIHJlcG86ICdteS1yZXBvJyxcbiAqIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBjbGFzcyBBbHdheXNPbldhcm1SdW5uZXIgZXh0ZW5kcyBDb25zdHJ1Y3Qge1xuICAvKipcbiAgICogVGhlIGZpbGwgcGF5bG9hZCBmb3IgdGhpcyB3YXJtIHJ1bm5lciBjb25maWd1cmF0aW9uLlxuICAgKiBAaW50ZXJuYWxcbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBfZmlsbFBheWxvYWQ6IFdhcm1SdW5uZXJGaWxsUGF5bG9hZDtcblxuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogQWx3YXlzT25XYXJtUnVubmVyUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuICAgIHRoaXMuX2ZpbGxQYXlsb2FkID0gYnVpbGRXYXJtUnVubmVyKHRoaXMsIHByb3BzLCBldmVudHMuU2NoZWR1bGUuY3Jvbih7IGhvdXI6ICcwJywgbWludXRlOiAnMCcgfSksIGNkay5EdXJhdGlvbi5kYXlzKDEpLnRvU2Vjb25kcygpLCB0cnVlKTtcbiAgfVxufVxuXG4vKipcbiAqIENvbnZlcnQgQVdTIEV2ZW50QnJpZGdlIGNyb24gZm9ybWF0IHRvIGNyb24tcGFyc2VyIGZvcm1hdC5cbiAqIEFXUzogY3JvbihtaW4gaG91ciBkb20gbW9udGggZG93IHllYXIpLCBjcm9uLXBhcnNlcjogc2VjIG1pbiBob3VyIGRvbSBtb250aCBkb3dcbiAqL1xuZnVuY3Rpb24gYXdzQ3JvblRvUGFyc2VyRm9ybWF0KGV4cHJlc3Npb25TdHJpbmc6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IG1hdGNoID0gZXhwcmVzc2lvblN0cmluZy5tYXRjaCgvXmNyb25cXCgoLispXFwpJC8pO1xuICBpZiAoIW1hdGNoKSByZXR1cm4gZXhwcmVzc2lvblN0cmluZztcbiAgY29uc3QgWywgaW5uZXJdID0gbWF0Y2g7XG4gIGNvbnN0IHBhcnRzID0gaW5uZXIudHJpbSgpLnNwbGl0KC9cXHMrLyk7XG4gIGlmIChwYXJ0cy5sZW5ndGggIT09IDYpIHJldHVybiBleHByZXNzaW9uU3RyaW5nO1xuICBjb25zdCBbbWludXRlLCBob3VyLCBkb20sIG1vbnRoLCBkb3ddID0gcGFydHM7XG4gIHJldHVybiBgMCAke21pbnV0ZX0gJHtob3VyfSAke2RvbX0gJHttb250aH0gJHtkb3d9YDtcbn1cblxuLyoqXG4gKiBQYXJzZSBBV1MgRXZlbnRCcmlkZ2UgcmF0ZSBleHByZXNzaW9uIGFuZCByZXR1cm4gaW50ZXJ2YWwgaW4gc2Vjb25kcy5cbiAqIEZvcm1hdDogcmF0ZSh2YWx1ZSB1bml0KSBlLmcuIHJhdGUoMiBob3VycyksIHJhdGUoNSBtaW51dGVzKSwgcmF0ZSgxIGRheSlcbiAqL1xuZnVuY3Rpb24gcGFyc2VSYXRlSW50ZXJ2YWwoZXhwcmVzc2lvblN0cmluZzogc3RyaW5nKTogbnVtYmVyIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgbWF0Y2ggPSBleHByZXNzaW9uU3RyaW5nLm1hdGNoKC9ecmF0ZVxcKChcXGQrKVxccysobWludXRlfG1pbnV0ZXN8aG91cnxob3Vyc3xkYXl8ZGF5cylcXCkkL2kpO1xuICBpZiAoIW1hdGNoKSByZXR1cm4gdW5kZWZpbmVkO1xuICBjb25zdCB2YWx1ZSA9IHBhcnNlSW50KG1hdGNoWzFdLCAxMCk7XG4gIGNvbnN0IHVuaXQgPSBtYXRjaFsyXS50b0xvd2VyQ2FzZSgpO1xuICBpZiAodmFsdWUgPD0gMCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgY29uc3Qgc2Vjb25kc1BlclVuaXQ6IFJlY29yZDxzdHJpbmcsIG51bWJlcj4gPSB7XG4gICAgbWludXRlOiA2MCxcbiAgICBtaW51dGVzOiA2MCxcbiAgICBob3VyOiAzNjAwLFxuICAgIGhvdXJzOiAzNjAwLFxuICAgIGRheTogODY0MDAsXG4gICAgZGF5czogODY0MDAsXG4gIH07XG4gIHJldHVybiB2YWx1ZSAqIHNlY29uZHNQZXJVbml0W3VuaXRdO1xufVxuXG4vKipcbiAqIEdldCB0aGUgaW50ZXJ2YWwgYmV0d2VlbiBzY2hlZHVsZSBvY2N1cnJlbmNlcyBpbiBzZWNvbmRzLlxuICogU3VwcG9ydHMgYm90aCBjcm9uIGFuZCByYXRlIGV4cHJlc3Npb25zLlxuICovXG5mdW5jdGlvbiBnZXRTY2hlZHVsZUludGVydmFsU2Vjb25kcyhleHByZXNzaW9uU3RyaW5nOiBzdHJpbmcpOiBudW1iZXIgfCB1bmRlZmluZWQge1xuICBjb25zdCByYXRlSW50ZXJ2YWwgPSBwYXJzZVJhdGVJbnRlcnZhbChleHByZXNzaW9uU3RyaW5nKTtcbiAgaWYgKHJhdGVJbnRlcnZhbCAhPT0gdW5kZWZpbmVkKSByZXR1cm4gcmF0ZUludGVydmFsO1xuXG4gIHRyeSB7XG4gICAgY29uc3QgY3JvbkV4cHJlc3Npb24gPSBDcm9uRXhwcmVzc2lvblBhcnNlci5wYXJzZShhd3NDcm9uVG9QYXJzZXJGb3JtYXQoZXhwcmVzc2lvblN0cmluZykpO1xuICAgIGNvbnN0IG5leHQgPSBjcm9uRXhwcmVzc2lvbi50YWtlKDIpO1xuICAgIHJldHVybiAobmV4dFsxXS5nZXRUaW1lKCkgLSBuZXh0WzBdLmdldFRpbWUoKSkgLyAxMDAwO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG59XG5cbi8qKlxuICogV2FybSBydW5uZXJzIGFjdGl2ZSBkdXJpbmcgYSB0aW1lIHdpbmRvdyBzcGVjaWZpZWQgYnkgc3RhcnQgdGltZSAoYHNjaGVkdWxlYCkgYW5kIGR1cmF0aW9uIChgZHVyYXRpb25gKS5cbiAqXG4gKiBSdW5uZXJzIHdpbGwgYmUgcHJvdmlzaW9uZWQgdXNpbmcgdGhlIHNwZWNpZmllZCBwcm92aWRlciBhbmQgcmVnaXN0ZXJlZCBpbiB0aGUgc3BlY2lmaWVkIHJlcG9zaXRvcnkgb3Igb3JnYW5pemF0aW9uLlxuICpcbiAqIFJlZ2lzdHJhdGlvbiBsZXZlbCBtdXN0IG1hdGNoIHRoZSBvbmUgc2VsZWN0ZWQgZHVyaW5nIHNldHVwLlxuICpcbiAqIEBzZWUgaHR0cHM6Ly9naXRodWIuY29tL0Nsb3VkU25vcmtlbC9jZGstZ2l0aHViLXJ1bm5lcnMvYmxvYi9tYWluL1NFVFVQX0dJVEhVQi5tZFxuICpcbiAqICMjIExpbWl0YXRpb25zXG4gKlxuICogLSAqKk5vIGRlcGxveW1lbnQtZmlsbCoqOiBVbmxpa2UgYEFsd2F5c09uV2FybVJ1bm5lcmAsIHNjaGVkdWxlZCB3YXJtIHJ1bm5lcnMgZG8gbm90IGdldCBhbiBpbml0aWFsXG4gKiAgIGZpbGwgb24gZGVwbG95LiBUaGUgZmlyc3QgZmlsbCBoYXBwZW5zIGF0IHRoZSBuZXh0IHNjaGVkdWxlIG9jY3VycmVuY2UuIElmIHlvdSBkZXBsb3kgYXQgMXBtIGZvclxuICogICBhIDJwbSBzY2hlZHVsZSwgcnVubmVycyB3aWxsIG5vdCBhcHBlYXIgdW50aWwgMnBtLlxuICogLSBKb2JzIHdpbGwgc3RpbGwgdHJpZ2dlciBwcm92aXNpb25pbmcgb2Ygb24tZGVtYW5kIHJ1bm5lcnMsIGV2ZW4gaWYgYSB3YXJtIHJ1bm5lciBlbmRzIHVwIGJlaW5nIHVzZWQuXG4gKiAtIFlvdSBtYXkgYnJpZWZseSBzZWUgbW9yZSB0aGFuIGBjb3VudGAgcnVubmVycyB3aGVuIGNoYW5naW5nIGNvbmZpZyBvciBhdCByb3RhdGlvbi5cbiAqIC0gVG8gcmVtb3ZlOiBzZXQgYGNvdW50YCB0byAwLCBkZXBsb3ksIHdhaXQgZm9yIHdhcm0gcnVubmVycyB0byBzdG9wLCB0aGVuIHJlbW92ZSBhbmQgZGVwbG95IGFnYWluLlxuICogICBJZiB5b3UgZG9uJ3QgZm9sbG93IHRoaXMgcHJvY2VkdXJlLCB3YXJtIHJ1bm5lcnMgbWF5IGxpbmdlciB1bnRpbCB0aGV5IGV4cGlyZS5cbiAqIC0gUHJvdmlkZXIgZmFpbHVyZXMgb3IgdGltZW91dHMgKGxpa2UgTGFtYmRhIHByb3ZpZGVyIHRpbWluZyBvdXQgYWZ0ZXIgMTUgbWludXRlcykgd2lsbCByZXN1bHQgaW4gYVxuICogICBnYXAgaW4gY292ZXJhZ2UgdW50aWwgdGhlIHJldHJ5IHN1Y2NlZWRzLiBDdXJyZW50IHJldHJ5IG1lY2hhbmlzbSBoYXMgYnVpbHQtaW4gYmFjay1vZmYgcmF0ZSBhbmRcbiAqICAgY2FuIGJlIHR3ZWFrZWQgdXNpbmcgYHJldHJ5T3B0aW9uc2AuIFRoaXMgd2lsbCBiZSBpbXByb3ZlZCBpbiB0aGUgZnV0dXJlLlxuICpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIENyb246IGZpbGwgYXQgMXBtIG9uIHdlZWtkYXlzXG4gKiBuZXcgU2NoZWR1bGVkV2FybVJ1bm5lcihzdGFjaywgJ0J1c2luZXNzIEhvdXJzJywge1xuICogICBydW5uZXJzLFxuICogICBwcm92aWRlcjogbXlQcm92aWRlcixcbiAqICAgY291bnQ6IDMsXG4gKiAgIG93bmVyOiAnbXktb3JnJyxcbiAqICAgcmVwbzogJ215LXJlcG8nLFxuICogICBzY2hlZHVsZTogZXZlbnRzLlNjaGVkdWxlLmNyb24oeyBob3VyOiAnMTMnLCBtaW51dGU6ICcwJywgd2Vla0RheTogJ01PTi1GUkknIH0pLFxuICogICBkdXJhdGlvbjogY2RrLkR1cmF0aW9uLmhvdXJzKDIpLFxuICogfSk7XG4gKiBgYGBcbiAqXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBSYXRlOiBmaWxsIGV2ZXJ5IDEyIGhvdXJzXG4gKiBuZXcgU2NoZWR1bGVkV2FybVJ1bm5lcihzdGFjaywgJ0V2ZXJ5IDEyIEhvdXJzJywge1xuICogICBydW5uZXJzLFxuICogICBwcm92aWRlcjogbXlQcm92aWRlcixcbiAqICAgY291bnQ6IDIsXG4gKiAgIG93bmVyOiAnbXktb3JnJyxcbiAqICAgcmVwbzogJ215LXJlcG8nLFxuICogICBzY2hlZHVsZTogZXZlbnRzLlNjaGVkdWxlLnJhdGUoY2RrLkR1cmF0aW9uLmhvdXJzKDUpKSxcbiAqICAgZHVyYXRpb246IGNkay5EdXJhdGlvbi5ob3VycygxMiksXG4gKiB9KTtcbiAqIGBgYFxuICovXG5leHBvcnQgY2xhc3MgU2NoZWR1bGVkV2FybVJ1bm5lciBleHRlbmRzIENvbnN0cnVjdCB7XG4gIC8qKlxuICAgKiBUaGUgZmlsbCBwYXlsb2FkIGZvciB0aGlzIHdhcm0gcnVubmVyIGNvbmZpZ3VyYXRpb24uXG4gICAqIEBpbnRlcm5hbFxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IF9maWxsUGF5bG9hZDogV2FybVJ1bm5lckZpbGxQYXlsb2FkO1xuXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHByb3BzOiBTY2hlZHVsZWRXYXJtUnVubmVyUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuXG4gICAgLy8gbWFrZSBzdXJlIHRoZSBkdXJhdGlvbiBpcyBub3QgbG9uZ2VyIHRoYW4gdGhlIGludGVydmFsIGJldHdlZW4gbmV4dCB0d28gc2NoZWR1bGUgb2NjdXJyZW5jZXNcbiAgICBjb25zdCBpbnRlcnZhbCA9IGdldFNjaGVkdWxlSW50ZXJ2YWxTZWNvbmRzKHByb3BzLnNjaGVkdWxlLmV4cHJlc3Npb25TdHJpbmcpO1xuICAgIGlmIChpbnRlcnZhbCAhPT0gdW5kZWZpbmVkICYmIGludGVydmFsIDwgcHJvcHMuZHVyYXRpb24udG9TZWNvbmRzKCkpIHtcbiAgICAgIGNkay5Bbm5vdGF0aW9ucy5vZih0aGlzKS5hZGRFcnJvcihgU2NoZWR1bGVkV2FybVJ1bm5lciBkdXJhdGlvbiAke3Byb3BzLmR1cmF0aW9uLnRvSHVtYW5TdHJpbmcoKX0gaXMgbG9uZ2VyIHRoYW4gdGhlIGludGVydmFsICR7Y2RrLkR1cmF0aW9uLnNlY29uZHMoaW50ZXJ2YWwpLnRvSHVtYW5TdHJpbmcoKX0gYmV0d2VlbiBuZXh0IHR3byBzY2hlZHVsZSBvY2N1cnJlbmNlcy4gVGhpcyB3aWxsIHJlc3VsdCBpbiBvdmVybGFwcGluZyB3YXJtIHJ1bm5lcnMgYXQgdGhlIHN0YXJ0IG9mIHRoZSBuZXh0IHNjaGVkdWxlIG9jY3VycmVuY2UuYCk7XG4gICAgfVxuXG4gICAgLy8gd2FybiBmb3Igc2hvcnQgaW50ZXJ2YWxcbiAgICBpZiAoaW50ZXJ2YWwgIT09IHVuZGVmaW5lZCAmJiBpbnRlcnZhbCA8IGNkay5EdXJhdGlvbi5ob3VycygxKS50b1NlY29uZHMoKSkge1xuICAgICAgY2RrLkFubm90YXRpb25zLm9mKHRoaXMpLmFkZFdhcm5pbmdWMihcbiAgICAgICAgJ0BjbG91ZHNub3JrZWwvY2RrLWdpdGh1Yi1ydW5uZXJzOlNjaGVkdWxlZFdhcm1SdW5uZXIuaW50ZXJ2YWxUb29TaG9ydCcsXG4gICAgICAgIGBTY2hlZHVsZWRXYXJtUnVubmVyIGludGVydmFsICR7Y2RrLkR1cmF0aW9uLnNlY29uZHMoaW50ZXJ2YWwpLnRvSHVtYW5TdHJpbmcoKX0gaXMgbGVzcyB0aGFuIDEgaG91ciwgd2hpY2ggbWF5IHJlc3VsdCBpbiBtb3JlIHdhcm0gcnVubmVycyB0aGFuIGV4cGVjdGVkYCxcbiAgICAgICk7XG4gICAgfVxuXG4gICAgdGhpcy5fZmlsbFBheWxvYWQgPSBidWlsZFdhcm1SdW5uZXIodGhpcywgcHJvcHMsIHByb3BzLnNjaGVkdWxlLCBwcm9wcy5kdXJhdGlvbi50b1NlY29uZHMoKSwgZmFsc2UpO1xuICB9XG59XG4iXX0=