projen-pipelines 0.2.12 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +5703 -1380
- package/API.md +5813 -1492
- package/README.md +253 -0
- package/docs/drift-detection.md +264 -0
- package/lib/assign-approver/base.js +1 -1
- package/lib/assign-approver/github.js +1 -1
- package/lib/awscdk/base.d.ts +21 -0
- package/lib/awscdk/base.js +246 -2
- package/lib/awscdk/bash.js +1 -1
- package/lib/awscdk/github.js +1 -1
- package/lib/awscdk/gitlab.js +1 -1
- package/lib/drift/base.d.ts +64 -0
- package/lib/drift/base.js +18 -0
- package/lib/drift/bash.d.ts +15 -0
- package/lib/drift/bash.js +170 -0
- package/lib/drift/detect-drift.d.ts +54 -0
- package/lib/drift/detect-drift.js +259 -0
- package/lib/drift/github.d.ts +21 -0
- package/lib/drift/github.js +232 -0
- package/lib/drift/gitlab.d.ts +20 -0
- package/lib/drift/gitlab.js +138 -0
- package/lib/drift/index.d.ts +5 -0
- package/lib/drift/index.js +22 -0
- package/lib/drift/step.d.ts +14 -0
- package/lib/drift/step.js +48 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +3 -1
- package/lib/steps/artifact-steps.js +2 -2
- package/lib/steps/aws-assume-role.step.js +1 -1
- package/lib/steps/registries.js +2 -2
- package/lib/steps/step.d.ts +6 -1
- package/lib/steps/step.js +14 -10
- package/lib/versioning/computation.d.ts +63 -0
- package/lib/versioning/computation.js +121 -0
- package/lib/versioning/config.d.ts +41 -0
- package/lib/versioning/config.js +91 -0
- package/lib/versioning/index.d.ts +7 -0
- package/lib/versioning/index.js +46 -0
- package/lib/versioning/outputs.d.ts +87 -0
- package/lib/versioning/outputs.js +166 -0
- package/lib/versioning/setup.d.ts +30 -0
- package/lib/versioning/setup.js +165 -0
- package/lib/versioning/strategy.d.ts +21 -0
- package/lib/versioning/strategy.js +51 -0
- package/lib/versioning/types.d.ts +183 -0
- package/lib/versioning/types.js +3 -0
- package/lib/versioning/version-info.d.ts +106 -0
- package/lib/versioning/version-info.js +269 -0
- package/package.json +2 -1
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.DriftDetector = void 0;
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
class DriftDetector {
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.results = [];
|
|
10
|
+
this.options = {
|
|
11
|
+
timeout: 30,
|
|
12
|
+
failOnDrift: true,
|
|
13
|
+
...options,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
async run() {
|
|
17
|
+
console.log(`Starting drift detection in region ${this.options.region}`);
|
|
18
|
+
try {
|
|
19
|
+
const stacks = await this.getStacksToCheck();
|
|
20
|
+
for (const stackName of stacks) {
|
|
21
|
+
await this.checkStackDrift(stackName);
|
|
22
|
+
}
|
|
23
|
+
this.printSummary();
|
|
24
|
+
this.saveResults();
|
|
25
|
+
if (this.shouldFail()) {
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
console.error('Fatal error during drift detection:', error);
|
|
31
|
+
process.exit(2);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async getStacksToCheck() {
|
|
35
|
+
if (this.options.stackNames && this.options.stackNames.length > 0) {
|
|
36
|
+
return this.options.stackNames;
|
|
37
|
+
}
|
|
38
|
+
console.log('Getting all stacks in the region...');
|
|
39
|
+
const output = (0, child_process_1.execSync)(`aws cloudformation list-stacks --region ${this.options.region} --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE --query 'StackSummaries[].StackName' --output json`, { encoding: 'utf8' });
|
|
40
|
+
return JSON.parse(output);
|
|
41
|
+
}
|
|
42
|
+
async checkStackDrift(stackName) {
|
|
43
|
+
console.log(`\nChecking drift for stack: ${stackName}`);
|
|
44
|
+
const result = {
|
|
45
|
+
stackName,
|
|
46
|
+
driftStatus: 'NOT_CHECKED',
|
|
47
|
+
};
|
|
48
|
+
try {
|
|
49
|
+
// Start drift detection
|
|
50
|
+
const driftId = await this.startDriftDetection(stackName);
|
|
51
|
+
console.log(`Started drift detection with ID: ${driftId}`);
|
|
52
|
+
// Wait for completion
|
|
53
|
+
const driftStatus = await this.waitForDriftDetection(driftId);
|
|
54
|
+
result.driftStatus = driftStatus;
|
|
55
|
+
if (driftStatus === 'DRIFTED') {
|
|
56
|
+
result.driftedResources = await this.getDriftedResources(stackName);
|
|
57
|
+
console.log(`DRIFT DETECTED in stack ${stackName}!`);
|
|
58
|
+
// Handle known drift errors
|
|
59
|
+
result.knownErrorsHandled = await this.handleKnownDriftErrors(result.driftedResources);
|
|
60
|
+
// Print drift details
|
|
61
|
+
this.printDriftDetails(result);
|
|
62
|
+
}
|
|
63
|
+
else if (driftStatus === 'IN_SYNC') {
|
|
64
|
+
console.log(`Stack ${stackName} is in sync`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error(`Error checking drift for stack ${stackName}:`, error.message);
|
|
69
|
+
result.error = error.message;
|
|
70
|
+
}
|
|
71
|
+
this.results.push(result);
|
|
72
|
+
}
|
|
73
|
+
async startDriftDetection(stackName) {
|
|
74
|
+
const output = (0, child_process_1.execSync)(`aws cloudformation detect-stack-drift --stack-name ${stackName} --region ${this.options.region} --query 'StackDriftDetectionId' --output text`, { encoding: 'utf8' });
|
|
75
|
+
return output.trim();
|
|
76
|
+
}
|
|
77
|
+
async waitForDriftDetection(driftId) {
|
|
78
|
+
const timeout = this.options.timeout * 60; // Convert to seconds
|
|
79
|
+
const startTime = Date.now();
|
|
80
|
+
while (true) {
|
|
81
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
82
|
+
if (elapsed > timeout) {
|
|
83
|
+
throw new Error(`Drift detection timed out after ${this.options.timeout} minutes`);
|
|
84
|
+
}
|
|
85
|
+
const output = (0, child_process_1.execSync)(`aws cloudformation describe-stack-drift-detection-status --stack-drift-detection-id ${driftId} --region ${this.options.region} --output json`, { encoding: 'utf8' });
|
|
86
|
+
const status = JSON.parse(output);
|
|
87
|
+
console.log(`Drift detection status: ${status.DetectionStatus} (${elapsed}s elapsed)`);
|
|
88
|
+
if (status.DetectionStatus === 'DETECTION_COMPLETE') {
|
|
89
|
+
return status.StackDriftStatus;
|
|
90
|
+
}
|
|
91
|
+
else if (status.DetectionStatus === 'DETECTION_FAILED') {
|
|
92
|
+
throw new Error(`Drift detection failed: ${status.DetectionStatusReason}`);
|
|
93
|
+
}
|
|
94
|
+
// Wait 10 seconds before checking again
|
|
95
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async getDriftedResources(stackName) {
|
|
99
|
+
const output = (0, child_process_1.execSync)(`aws cloudformation describe-stack-resource-drifts --stack-name ${stackName} --region ${this.options.region} --stack-resource-drift-status-filters MODIFIED DELETED --output json`, { encoding: 'utf8' });
|
|
100
|
+
const response = JSON.parse(output);
|
|
101
|
+
return response.StackResourceDrifts.map((drift) => ({
|
|
102
|
+
logicalResourceId: drift.LogicalResourceId,
|
|
103
|
+
resourceType: drift.ResourceType,
|
|
104
|
+
stackResourceDriftStatus: drift.StackResourceDriftStatus,
|
|
105
|
+
propertyDifferences: drift.PropertyDifferences?.map((diff) => ({
|
|
106
|
+
propertyPath: diff.PropertyPath,
|
|
107
|
+
expectedValue: diff.ExpectedValue,
|
|
108
|
+
actualValue: diff.ActualValue,
|
|
109
|
+
differenceType: diff.DifferenceType,
|
|
110
|
+
})),
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Handle known drift errors for specific resources
|
|
115
|
+
* This method can be extended later to implement custom logic for known issues
|
|
116
|
+
*/
|
|
117
|
+
async handleKnownDriftErrors(driftedResources) {
|
|
118
|
+
const knownErrors = [];
|
|
119
|
+
if (!driftedResources) {
|
|
120
|
+
return knownErrors;
|
|
121
|
+
}
|
|
122
|
+
for (const _resource of driftedResources) {
|
|
123
|
+
// TODO: Implement custom logic here for known drift errors
|
|
124
|
+
// Example structure for handling known errors:
|
|
125
|
+
// Check for Lambda runtime drift (common issue)
|
|
126
|
+
// if (resource.resourceType === 'AWS::Lambda::Function' && resource.propertyDifferences) {
|
|
127
|
+
// const runtimeDrift = resource.propertyDifferences.find(diff =>
|
|
128
|
+
// diff.propertyPath === '/Runtime' || diff.propertyPath === 'Runtime',
|
|
129
|
+
// );
|
|
130
|
+
// if (runtimeDrift) {
|
|
131
|
+
// knownErrors.push({
|
|
132
|
+
// resourceId: resource.logicalResourceId,
|
|
133
|
+
// errorType: 'lambda-runtime-drift',
|
|
134
|
+
// originalError: runtimeDrift,
|
|
135
|
+
// handled: false, // Will be implemented later
|
|
136
|
+
// message: 'Lambda runtime drift detected - manual implementation needed',
|
|
137
|
+
// });
|
|
138
|
+
// }
|
|
139
|
+
// }
|
|
140
|
+
// // Check for auto-scaling related drift
|
|
141
|
+
// if (resource.resourceType.includes('AutoScaling') && resource.propertyDifferences) {
|
|
142
|
+
// knownErrors.push({
|
|
143
|
+
// resourceId: resource.logicalResourceId,
|
|
144
|
+
// errorType: 'autoscaling-drift',
|
|
145
|
+
// originalError: resource.propertyDifferences,
|
|
146
|
+
// handled: false, // Will be implemented later
|
|
147
|
+
// message: 'Auto-scaling drift detected - manual implementation needed',
|
|
148
|
+
// });
|
|
149
|
+
// }
|
|
150
|
+
// Add more patterns here as needed
|
|
151
|
+
}
|
|
152
|
+
return knownErrors;
|
|
153
|
+
}
|
|
154
|
+
printDriftDetails(result) {
|
|
155
|
+
if (!result.driftedResources || result.driftedResources.length === 0) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
console.log('\nDrifted resources:');
|
|
159
|
+
console.log('=================');
|
|
160
|
+
for (const resource of result.driftedResources) {
|
|
161
|
+
console.log(`\n- ${resource.logicalResourceId} (${resource.resourceType})`);
|
|
162
|
+
console.log(` Status: ${resource.stackResourceDriftStatus}`);
|
|
163
|
+
if (resource.propertyDifferences) {
|
|
164
|
+
console.log(' Property differences:');
|
|
165
|
+
for (const diff of resource.propertyDifferences) {
|
|
166
|
+
console.log(` ${diff.propertyPath}:`);
|
|
167
|
+
console.log(` Expected: ${diff.expectedValue}`);
|
|
168
|
+
console.log(` Actual: ${diff.actualValue}`);
|
|
169
|
+
console.log(` Type: ${diff.differenceType}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
printSummary() {
|
|
175
|
+
console.log('\n========== DRIFT DETECTION SUMMARY ==========');
|
|
176
|
+
const driftedStacks = this.results.filter(r => r.driftStatus === 'DRIFTED');
|
|
177
|
+
const syncedStacks = this.results.filter(r => r.driftStatus === 'IN_SYNC');
|
|
178
|
+
const errorStacks = this.results.filter(r => r.error);
|
|
179
|
+
console.log(`Total stacks checked: ${this.results.length}`);
|
|
180
|
+
console.log(`In sync: ${syncedStacks.length}`);
|
|
181
|
+
console.log(`Drifted: ${driftedStacks.length}`);
|
|
182
|
+
console.log(`Errors: ${errorStacks.length}`);
|
|
183
|
+
if (driftedStacks.length > 0) {
|
|
184
|
+
console.log('\nDrifted stacks:');
|
|
185
|
+
for (const stack of driftedStacks) {
|
|
186
|
+
const resourceCount = stack.driftedResources?.length || 0;
|
|
187
|
+
console.log(` - ${stack.stackName} (${resourceCount} resources)`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (errorStacks.length > 0) {
|
|
191
|
+
console.log('\nStacks with errors:');
|
|
192
|
+
for (const stack of errorStacks) {
|
|
193
|
+
console.log(` - ${stack.stackName}: ${stack.error}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
saveResults() {
|
|
198
|
+
const outputFile = process.env.DRIFT_DETECTION_OUTPUT || 'drift-detection-results.json';
|
|
199
|
+
(0, fs_1.writeFileSync)(outputFile, JSON.stringify(this.results, null, 2));
|
|
200
|
+
console.log(`\nResults saved to: ${outputFile}`);
|
|
201
|
+
}
|
|
202
|
+
shouldFail() {
|
|
203
|
+
if (!this.options.failOnDrift) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
return this.results.some(r => r.driftStatus === 'DRIFTED');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
exports.DriftDetector = DriftDetector;
|
|
210
|
+
// Parse command line arguments
|
|
211
|
+
function parseArgs() {
|
|
212
|
+
const args = process.argv.slice(2);
|
|
213
|
+
const options = {
|
|
214
|
+
region: process.env.AWS_REGION || 'us-east-1',
|
|
215
|
+
};
|
|
216
|
+
for (let i = 0; i < args.length; i++) {
|
|
217
|
+
switch (args[i]) {
|
|
218
|
+
case '--region':
|
|
219
|
+
options.region = args[++i];
|
|
220
|
+
break;
|
|
221
|
+
case '--stacks':
|
|
222
|
+
options.stackNames = args[++i].split(',');
|
|
223
|
+
break;
|
|
224
|
+
case '--timeout':
|
|
225
|
+
options.timeout = parseInt(args[++i]);
|
|
226
|
+
break;
|
|
227
|
+
case '--no-fail-on-drift':
|
|
228
|
+
options.failOnDrift = false;
|
|
229
|
+
break;
|
|
230
|
+
default:
|
|
231
|
+
console.error(`Unknown argument: ${args[i]}`);
|
|
232
|
+
printUsage();
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return options;
|
|
237
|
+
}
|
|
238
|
+
function printUsage() {
|
|
239
|
+
console.log(`
|
|
240
|
+
Usage: detect-drift.ts [options]
|
|
241
|
+
|
|
242
|
+
Options:
|
|
243
|
+
--region <region> AWS region (default: us-east-1 or AWS_REGION env var)
|
|
244
|
+
--stacks <stack1,stack2> Comma-separated list of stack names (default: all stacks)
|
|
245
|
+
--timeout <minutes> Timeout in minutes (default: 30)
|
|
246
|
+
--no-fail-on-drift Don't exit with error code if drift is detected
|
|
247
|
+
|
|
248
|
+
Environment variables:
|
|
249
|
+
AWS_REGION Default AWS region
|
|
250
|
+
DRIFT_DETECTION_OUTPUT Output file path (default: drift-detection-results.json)
|
|
251
|
+
`);
|
|
252
|
+
}
|
|
253
|
+
// Main entry point
|
|
254
|
+
if (require.main === module) {
|
|
255
|
+
const options = parseArgs();
|
|
256
|
+
const detector = new DriftDetector(options);
|
|
257
|
+
detector.run().catch(console.error);
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGV0ZWN0LWRyaWZ0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2RyaWZ0L2RldGVjdC1kcmlmdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBRUEsaURBQXlDO0FBQ3pDLDJCQUFtQztBQXVDbkMsTUFBTSxhQUFhO0lBSWpCLFlBQVksT0FBOEI7UUFGekIsWUFBTyxHQUFrQixFQUFFLENBQUM7UUFHM0MsSUFBSSxDQUFDLE9BQU8sR0FBRztZQUNiLE9BQU8sRUFBRSxFQUFFO1lBQ1gsV0FBVyxFQUFFLElBQUk7WUFDakIsR0FBRyxPQUFPO1NBQ1gsQ0FBQztJQUNKLENBQUM7SUFFTSxLQUFLLENBQUMsR0FBRztRQUNkLE9BQU8sQ0FBQyxHQUFHLENBQUMsc0NBQXNDLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUV6RSxJQUFJLENBQUM7WUFDSCxNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBRTdDLEtBQUssTUFBTSxTQUFTLElBQUksTUFBTSxFQUFFLENBQUM7Z0JBQy9CLE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUN4QyxDQUFDO1lBRUQsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQ3BCLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUVuQixJQUFJLElBQUksQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDO2dCQUN0QixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2xCLENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE9BQU8sQ0FBQyxLQUFLLENBQUMscUNBQXFDLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDNUQsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNsQixDQUFDO0lBQ0gsQ0FBQztJQUVPLEtBQUssQ0FBQyxnQkFBZ0I7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QUNILENBQUM7QUFFRCxtQkFBbUI7QUFDbkIsSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLE1BQU0sRUFBRSxDQUFDO0lBQzVCLE1BQU0sT0FBTyxHQUFHLFNBQVMsRUFBRSxDQUFDO0lBQzVCLE1BQU0sUUFBUSxHQUFHLElBQUksYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzVDLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQ3RDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIjIS91c3IvYmluL2VudiBub2RlXG5cbmltcG9ydCB7IGV4ZWNTeW5jIH0gZnJvbSAnY2hpbGRfcHJvY2Vzcyc7XG5pbXBvcnQgeyB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xuXG5pbnRlcmZhY2UgRHJpZnREZXRlY3Rpb25PcHRpb25zIHtcbiAgcmVnaW9uOiBzdHJpbmc7XG4gIHN0YWNrTmFtZXM/OiBzdHJpbmdbXTtcbiAgdGltZW91dD86IG51bWJlcjtcbiAgZmFpbE9uRHJpZnQ/OiBib29sZWFuO1xufVxuXG5pbnRlcmZhY2UgRHJpZnRSZXN1bHQge1xuICBzdGFja05hbWU6IHN0cmluZztcbiAgZHJpZnRTdGF0dXM6ICdJTl9TWU5DJyB8ICdEUklGVEVEJyB8ICdVTktOT1dOJyB8ICdOT1RfQ0hFQ0tFRCc7XG4gIGRyaWZ0ZWRSZXNvdXJjZXM/OiBEcmlmdGVkUmVzb3VyY2VbXTtcbiAgZXJyb3I/OiBzdHJpbmc7XG4gIGtub3duRXJyb3JzSGFuZGxlZD86IEtub3duRXJyb3JSZXN1bHRbXTtcbn1cblxuaW50ZXJmYWNlIEtub3duRXJyb3JSZXN1bHQge1xuICByZWFkb25seSByZXNvdXJjZUlkOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGVycm9yVHlwZTogc3RyaW5nO1xuICByZWFkb25seSBvcmlnaW5hbEVycm9yOiBhbnk7XG4gIHJlYWRvbmx5IGhhbmRsZWQ6IGJvb2xlYW47XG4gIHJlYWRvbmx5IG1lc3NhZ2U/OiBzdHJpbmc7XG59XG5cbmludGVyZmFjZSBEcmlmdGVkUmVzb3VyY2Uge1xuICByZWFkb25seSBsb2dpY2FsUmVzb3VyY2VJZDogc3RyaW5nO1xuICByZWFkb25seSByZXNvdXJjZVR5cGU6IHN0cmluZztcbiAgcmVhZG9ubHkgc3RhY2tSZXNvdXJjZURyaWZ0U3RhdHVzOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHByb3BlcnR5RGlmZmVyZW5jZXM/OiBQcm9wZXJ0eURpZmZlcmVuY2VbXTtcbn1cblxuaW50ZXJmYWNlIFByb3BlcnR5RGlmZmVyZW5jZSB7XG4gIHJlYWRvbmx5IHByb3BlcnR5UGF0aDogc3RyaW5nO1xuICByZWFkb25seSBleHBlY3RlZFZhbHVlOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGFjdHVhbFZhbHVlOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGRpZmZlcmVuY2VUeXBlOiBzdHJpbmc7XG59XG5cbmNsYXNzIERyaWZ0RGV0ZWN0b3Ige1xuICBwcml2YXRlIHJlYWRvbmx5IG9wdGlvbnM6IERyaWZ0RGV0ZWN0aW9uT3B0aW9ucztcbiAgcHJpdmF0ZSByZWFkb25seSByZXN1bHRzOiBEcmlmdFJlc3VsdFtdID0gW107XG5cbiAgY29uc3RydWN0b3Iob3B0aW9uczogRHJpZnREZXRlY3Rpb25PcHRpb25zKSB7XG4gICAgdGhpcy5vcHRpb25zID0ge1xuICAgICAgdGltZW91dDogMzAsXG4gICAgICBmYWlsT25EcmlmdDogdHJ1ZSxcbiAgICAgIC4uLm9wdGlvbnMsXG4gICAgfTtcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBydW4oKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc29sZS5sb2coYFN0YXJ0aW5nIGRyaWZ0IGRldGVjdGlvbiBpbiByZWdpb24gJHt0aGlzLm9wdGlvbnMucmVnaW9ufWApO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHN0YWNrcyA9IGF3YWl0IHRoaXMuZ2V0U3RhY2tzVG9DaGVjaygpO1xuXG4gICAgICBmb3IgKGNvbnN0IHN0YWNrTmFtZSBvZiBzdGFja3MpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5jaGVja1N0YWNrRHJpZnQoc3RhY2tOYW1lKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5wcmludFN1bW1hcnkoKTtcbiAgICAgIHRoaXMuc2F2ZVJlc3VsdHMoKTtcblxuICAgICAgaWYgKHRoaXMuc2hvdWxkRmFpbCgpKSB7XG4gICAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcignRmF0YWwgZXJyb3IgZHVyaW5nIGRyaWZ0IGRldGVjdGlvbjonLCBlcnJvcik7XG4gICAgICBwcm9jZXNzLmV4aXQoMik7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBnZXRTdGFja3NUb0NoZWNrKCk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgICBpZiAodGhpcy5vcHRpb25zLnN0YWNrTmFtZXMgJiYgdGhpcy5vcHRpb25zLnN0YWNrTmFtZXMubGVuZ3RoID4gMCkge1xuICAgICAgcmV0dXJuIHRoaXMub3B0aW9ucy5zdGFja05hbWVzO1xuICAgIH1cblxuICAgIGNvbnNvbGUubG9nKCdHZXR0aW5nIGFsbCBzdGFja3MgaW4gdGhlIHJlZ2lvbi4uLicpO1xuICAgIGNvbnN0IG91dHB1dCA9IGV4ZWNTeW5jKFxuICAgICAgYGF3cyBjbG91ZGZvcm1hdGlvbiBsaXN0LXN0YWNrcyAtLXJlZ2lvbiAke3RoaXMub3B0aW9ucy5yZWdpb259IC0tc3RhY2stc3RhdHVzLWZpbHRlciBDUkVBVEVfQ09NUExFVEUgVVBEQVRFX0NPTVBMRVRFIC0tcXVlcnkgJ1N0YWNrU3VtbWFyaWVzW10uU3RhY2tOYW1lJyAtLW91dHB1dCBqc29uYCxcbiAgICAgIHsgZW5jb2Rpbmc6ICd1dGY4JyB9LFxuICAgICk7XG5cbiAgICByZXR1cm4gSlNPTi5wYXJzZShvdXRwdXQpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBjaGVja1N0YWNrRHJpZnQoc3RhY2tOYW1lOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zb2xlLmxvZyhgXFxuQ2hlY2tpbmcgZHJpZnQgZm9yIHN0YWNrOiAke3N0YWNrTmFtZX1gKTtcblxuICAgIGNvbnN0IHJlc3VsdDogRHJpZnRSZXN1bHQgPSB7XG4gICAgICBzdGFja05hbWUsXG4gICAgICBkcmlmdFN0YXR1czogJ05PVF9DSEVDS0VEJyxcbiAgICB9O1xuXG4gICAgdHJ5IHtcbiAgICAgIC8vIFN0YXJ0IGRyaWZ0IGRldGVjdGlvblxuICAgICAgY29uc3QgZHJpZnRJZCA9IGF3YWl0IHRoaXMuc3RhcnREcmlmdERldGVjdGlvbihzdGFja05hbWUpO1xuICAgICAgY29uc29sZS5sb2coYFN0YXJ0ZWQgZHJpZnQgZGV0ZWN0aW9uIHdpdGggSUQ6ICR7ZHJpZnRJZH1gKTtcblxuICAgICAgLy8gV2FpdCBmb3IgY29tcGxldGlvblxuICAgICAgY29uc3QgZHJpZnRTdGF0dXMgPSBhd2FpdCB0aGlzLndhaXRGb3JEcmlmdERldGVjdGlvbihkcmlmdElkKTtcbiAgICAgIHJlc3VsdC5kcmlmdFN0YXR1cyA9IGRyaWZ0U3RhdHVzO1xuXG4gICAgICBpZiAoZHJpZnRTdGF0dXMgPT09ICdEUklGVEVEJykge1xuICAgICAgICByZXN1bHQuZHJpZnRlZFJlc291cmNlcyA9IGF3YWl0IHRoaXMuZ2V0RHJpZnRlZFJlc291cmNlcyhzdGFja05hbWUpO1xuICAgICAgICBjb25zb2xlLmxvZyhgRFJJRlQgREVURUNURUQgaW4gc3RhY2sgJHtzdGFja05hbWV9IWApO1xuXG4gICAgICAgIC8vIEhhbmRsZSBrbm93biBkcmlmdCBlcnJvcnNcbiAgICAgICAgcmVzdWx0Lmtub3duRXJyb3JzSGFuZGxlZCA9IGF3YWl0IHRoaXMuaGFuZGxlS25vd25EcmlmdEVycm9ycyhyZXN1bHQuZHJpZnRlZFJlc291cmNlcyk7XG5cbiAgICAgICAgLy8gUHJpbnQgZHJpZnQgZGV0YWlsc1xuICAgICAgICB0aGlzLnByaW50RHJpZnREZXRhaWxzKHJlc3VsdCk7XG4gICAgICB9IGVsc2UgaWYgKGRyaWZ0U3RhdHVzID09PSAnSU5fU1lOQycpIHtcbiAgICAgICAgY29uc29sZS5sb2coYFN0YWNrICR7c3RhY2tOYW1lfSBpcyBpbiBzeW5jYCk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgY29uc29sZS5lcnJvcihgRXJyb3IgY2hlY2tpbmcgZHJpZnQgZm9yIHN0YWNrICR7c3RhY2tOYW1lfTpgLCBlcnJvci5tZXNzYWdlKTtcbiAgICAgIHJlc3VsdC5lcnJvciA9IGVycm9yLm1lc3NhZ2U7XG4gICAgfVxuXG4gICAgdGhpcy5yZXN1bHRzLnB1c2gocmVzdWx0KTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgc3RhcnREcmlmdERldGVjdGlvbihzdGFja05hbWU6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgY29uc3Qgb3V0cHV0ID0gZXhlY1N5bmMoXG4gICAgICBgYXdzIGNsb3VkZm9ybWF0aW9uIGRldGVjdC1zdGFjay1kcmlmdCAtLXN0YWNrLW5hbWUgJHtzdGFja05hbWV9IC0tcmVnaW9uICR7dGhpcy5vcHRpb25zLnJlZ2lvbn0gLS1xdWVyeSAnU3RhY2tEcmlmdERldGVjdGlvbklkJyAtLW91dHB1dCB0ZXh0YCxcbiAgICAgIHsgZW5jb2Rpbmc6ICd1dGY4JyB9LFxuICAgICk7XG5cbiAgICByZXR1cm4gb3V0cHV0LnRyaW0oKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgd2FpdEZvckRyaWZ0RGV0ZWN0aW9uKGRyaWZ0SWQ6IHN0cmluZyk6IFByb21pc2U8J0lOX1NZTkMnIHwgJ0RSSUZURUQnIHwgJ1VOS05PV04nIHwgJ05PVF9DSEVDS0VEJz4ge1xuICAgIGNvbnN0IHRpbWVvdXQgPSB0aGlzLm9wdGlvbnMudGltZW91dCEgKiA2MDsgLy8gQ29udmVydCB0byBzZWNvbmRzXG4gICAgY29uc3Qgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcblxuICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICBjb25zdCBlbGFwc2VkID0gTWF0aC5mbG9vcigoRGF0ZS5ub3coKSAtIHN0YXJ0VGltZSkgLyAxMDAwKTtcblxuICAgICAgaWYgKGVsYXBzZWQgPiB0aW1lb3V0KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRHJpZnQgZGV0ZWN0aW9uIHRpbWVkIG91dCBhZnRlciAke3RoaXMub3B0aW9ucy50aW1lb3V0fSBtaW51dGVzYCk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IG91dHB1dCA9IGV4ZWNTeW5jKFxuICAgICAgICBgYXdzIGNsb3VkZm9ybWF0aW9uIGRlc2NyaWJlLXN0YWNrLWRyaWZ0LWRldGVjdGlvbi1zdGF0dXMgLS1zdGFjay1kcmlmdC1kZXRlY3Rpb24taWQgJHtkcmlmdElkfSAtLXJlZ2lvbiAke3RoaXMub3B0aW9ucy5yZWdpb259IC0tb3V0cHV0IGpzb25gLFxuICAgICAgICB7IGVuY29kaW5nOiAndXRmOCcgfSxcbiAgICAgICk7XG5cbiAgICAgIGNvbnN0IHN0YXR1cyA9IEpTT04ucGFyc2Uob3V0cHV0KTtcbiAgICAgIGNvbnNvbGUubG9nKGBEcmlmdCBkZXRlY3Rpb24gc3RhdHVzOiAke3N0YXR1cy5EZXRlY3Rpb25TdGF0dXN9ICgke2VsYXBzZWR9cyBlbGFwc2VkKWApO1xuXG4gICAgICBpZiAoc3RhdHVzLkRldGVjdGlvblN0YXR1cyA9PT0gJ0RFVEVDVElPTl9DT01QTEVURScpIHtcbiAgICAgICAgcmV0dXJuIHN0YXR1cy5TdGFja0RyaWZ0U3RhdHVzIGFzICdJTl9TWU5DJyB8ICdEUklGVEVEJyB8ICdVTktOT1dOJyB8ICdOT1RfQ0hFQ0tFRCc7XG4gICAgICB9IGVsc2UgaWYgKHN0YXR1cy5EZXRlY3Rpb25TdGF0dXMgPT09ICdERVRFQ1RJT05fRkFJTEVEJykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYERyaWZ0IGRldGVjdGlvbiBmYWlsZWQ6ICR7c3RhdHVzLkRldGVjdGlvblN0YXR1c1JlYXNvbn1gKTtcbiAgICAgIH1cblxuICAgICAgLy8gV2FpdCAxMCBzZWNvbmRzIGJlZm9yZSBjaGVja2luZyBhZ2FpblxuICAgICAgYXdhaXQgbmV3IFByb21pc2UocmVzb2x2ZSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDEwMDAwKSk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBnZXREcmlmdGVkUmVzb3VyY2VzKHN0YWNrTmFtZTogc3RyaW5nKTogUHJvbWlzZTxEcmlmdGVkUmVzb3VyY2VbXT4ge1xuICAgIGNvbnN0IG91dHB1dCA9IGV4ZWNTeW5jKFxuICAgICAgYGF3cyBjbG91ZGZvcm1hdGlvbiBkZXNjcmliZS1zdGFjay1yZXNvdXJjZS1kcmlmdHMgLS1zdGFjay1uYW1lICR7c3RhY2tOYW1lfSAtLXJlZ2lvbiAke3RoaXMub3B0aW9ucy5yZWdpb259IC0tc3RhY2stcmVzb3VyY2UtZHJpZnQtc3RhdHVzLWZpbHRlcnMgTU9ESUZJRUQgREVMRVRFRCAtLW91dHB1dCBqc29uYCxcbiAgICAgIHsgZW5jb2Rpbmc6ICd1dGY4JyB9LFxuICAgICk7XG5cbiAgICBjb25zdCByZXNwb25zZSA9IEpTT04ucGFyc2Uob3V0cHV0KTtcbiAgICByZXR1cm4gcmVzcG9uc2UuU3RhY2tSZXNvdXJjZURyaWZ0cy5tYXAoKGRyaWZ0OiBhbnkpID0+ICh7XG4gICAgICBsb2dpY2FsUmVzb3VyY2VJZDogZHJpZnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgICByZXNvdXJjZVR5cGU6IGRyaWZ0LlJlc291cmNlVHlwZSxcbiAgICAgIHN0YWNrUmVzb3VyY2VEcmlmdFN0YXR1czogZHJpZnQuU3RhY2tSZXNvdXJjZURyaWZ0U3RhdHVzLFxuICAgICAgcHJvcGVydHlEaWZmZXJlbmNlczogZHJpZnQuUHJvcGVydHlEaWZmZXJlbmNlcz8ubWFwKChkaWZmOiBhbnkpID0+ICh7XG4gICAgICAgIHByb3BlcnR5UGF0aDogZGlmZi5Qcm9wZXJ0eVBhdGgsXG4gICAgICAgIGV4cGVjdGVkVmFsdWU6IGRpZmYuRXhwZWN0ZWRWYWx1ZSxcbiAgICAgICAgYWN0dWFsVmFsdWU6IGRpZmYuQWN0dWFsVmFsdWUsXG4gICAgICAgIGRpZmZlcmVuY2VUeXBlOiBkaWZmLkRpZmZlcmVuY2VUeXBlLFxuICAgICAgfSkpLFxuICAgIH0pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBIYW5kbGUga25vd24gZHJpZnQgZXJyb3JzIGZvciBzcGVjaWZpYyByZXNvdXJjZXNcbiAgICogVGhpcyBtZXRob2QgY2FuIGJlIGV4dGVuZGVkIGxhdGVyIHRvIGltcGxlbWVudCBjdXN0b20gbG9naWMgZm9yIGtub3duIGlzc3Vlc1xuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBoYW5kbGVLbm93bkRyaWZ0RXJyb3JzKGRyaWZ0ZWRSZXNvdXJjZXM/OiBEcmlmdGVkUmVzb3VyY2VbXSk6IFByb21pc2U8S25vd25FcnJvclJlc3VsdFtdPiB7XG4gICAgY29uc3Qga25vd25FcnJvcnM6IEtub3duRXJyb3JSZXN1bHRbXSA9IFtdO1xuXG4gICAgaWYgKCFkcmlmdGVkUmVzb3VyY2VzKSB7XG4gICAgICByZXR1cm4ga25vd25FcnJvcnM7XG4gICAgfVxuXG4gICAgZm9yIChjb25zdCBfcmVzb3VyY2Ugb2YgZHJpZnRlZFJlc291cmNlcykge1xuXG4gICAgICAvLyBUT0RPOiBJbXBsZW1lbnQgY3VzdG9tIGxvZ2ljIGhlcmUgZm9yIGtub3duIGRyaWZ0IGVycm9yc1xuICAgICAgLy8gRXhhbXBsZSBzdHJ1Y3R1cmUgZm9yIGhhbmRsaW5nIGtub3duIGVycm9yczpcblxuICAgICAgLy8gQ2hlY2sgZm9yIExhbWJkYSBydW50aW1lIGRyaWZ0IChjb21tb24gaXNzdWUpXG4gICAgICAvLyBpZiAocmVzb3VyY2UucmVzb3VyY2VUeXBlID09PSAnQVdTOjpMYW1iZGE6OkZ1bmN0aW9uJyAmJiByZXNvdXJjZS5wcm9wZXJ0eURpZmZlcmVuY2VzKSB7XG4gICAgICAvLyAgIGNvbnN0IHJ1bnRpbWVEcmlmdCA9IHJlc291cmNlLnByb3BlcnR5RGlmZmVyZW5jZXMuZmluZChkaWZmID0+XG4gICAgICAvLyAgICAgZGlmZi5wcm9wZXJ0eVBhdGggPT09ICcvUnVudGltZScgfHwgZGlmZi5wcm9wZXJ0eVBhdGggPT09ICdSdW50aW1lJyxcbiAgICAgIC8vICAgKTtcblxuICAgICAgLy8gICBpZiAocnVudGltZURyaWZ0KSB7XG4gICAgICAvLyAgICAga25vd25FcnJvcnMucHVzaCh7XG4gICAgICAvLyAgICAgICByZXNvdXJjZUlkOiByZXNvdXJjZS5sb2dpY2FsUmVzb3VyY2VJZCxcbiAgICAgIC8vICAgICAgIGVycm9yVHlwZTogJ2xhbWJkYS1ydW50aW1lLWRyaWZ0JyxcbiAgICAgIC8vICAgICAgIG9yaWdpbmFsRXJyb3I6IHJ1bnRpbWVEcmlmdCxcbiAgICAgIC8vICAgICAgIGhhbmRsZWQ6IGZhbHNlLCAvLyBXaWxsIGJlIGltcGxlbWVudGVkIGxhdGVyXG4gICAgICAvLyAgICAgICBtZXNzYWdlOiAnTGFtYmRhIHJ1bnRpbWUgZHJpZnQgZGV0ZWN0ZWQgLSBtYW51YWwgaW1wbGVtZW50YXRpb24gbmVlZGVkJyxcbiAgICAgIC8vICAgICB9KTtcbiAgICAgIC8vICAgfVxuICAgICAgLy8gfVxuXG4gICAgICAvLyAvLyBDaGVjayBmb3IgYXV0by1zY2FsaW5nIHJlbGF0ZWQgZHJpZnRcbiAgICAgIC8vIGlmIChyZXNvdXJjZS5yZXNvdXJjZVR5cGUuaW5jbHVkZXMoJ0F1dG9TY2FsaW5nJykgJiYgcmVzb3VyY2UucHJvcGVydHlEaWZmZXJlbmNlcykge1xuICAgICAgLy8gICBrbm93bkVycm9ycy5wdXNoKHtcbiAgICAgIC8vICAgICByZXNvdXJjZUlkOiByZXNvdXJjZS5sb2dpY2FsUmVzb3VyY2VJZCxcbiAgICAgIC8vICAgICBlcnJvclR5cGU6ICdhdXRvc2NhbGluZy1kcmlmdCcsXG4gICAgICAvLyAgICAgb3JpZ2luYWxFcnJvcjogcmVzb3VyY2UucHJvcGVydHlEaWZmZXJlbmNlcyxcbiAgICAgIC8vICAgICBoYW5kbGVkOiBmYWxzZSwgLy8gV2lsbCBiZSBpbXBsZW1lbnRlZCBsYXRlclxuICAgICAgLy8gICAgIG1lc3NhZ2U6ICdBdXRvLXNjYWxpbmcgZHJpZnQgZGV0ZWN0ZWQgLSBtYW51YWwgaW1wbGVtZW50YXRpb24gbmVlZGVkJyxcbiAgICAgIC8vICAgfSk7XG4gICAgICAvLyB9XG5cbiAgICAgIC8vIEFkZCBtb3JlIHBhdHRlcm5zIGhlcmUgYXMgbmVlZGVkXG4gICAgfVxuXG4gICAgcmV0dXJuIGtub3duRXJyb3JzO1xuICB9XG5cbiAgcHJpdmF0ZSBwcmludERyaWZ0RGV0YWlscyhyZXN1bHQ6IERyaWZ0UmVzdWx0KTogdm9pZCB7XG4gICAgaWYgKCFyZXN1bHQuZHJpZnRlZFJlc291cmNlcyB8fCByZXN1bHQuZHJpZnRlZFJlc291cmNlcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zb2xlLmxvZygnXFxuRHJpZnRlZCByZXNvdXJjZXM6Jyk7XG4gICAgY29uc29sZS5sb2coJz09PT09PT09PT09PT09PT09Jyk7XG5cbiAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIHJlc3VsdC5kcmlmdGVkUmVzb3VyY2VzKSB7XG4gICAgICBjb25zb2xlLmxvZyhgXFxuLSAke3Jlc291cmNlLmxvZ2ljYWxSZXNvdXJjZUlkfSAoJHtyZXNvdXJjZS5yZXNvdXJjZVR5cGV9KWApO1xuICAgICAgY29uc29sZS5sb2coYCAgU3RhdHVzOiAke3Jlc291cmNlLnN0YWNrUmVzb3VyY2VEcmlmdFN0YXR1c31gKTtcblxuICAgICAgaWYgKHJlc291cmNlLnByb3BlcnR5RGlmZmVyZW5jZXMpIHtcbiAgICAgICAgY29uc29sZS5sb2coJyAgUHJvcGVydHkgZGlmZmVyZW5jZXM6Jyk7XG4gICAgICAgIGZvciAoY29uc3QgZGlmZiBvZiByZXNvdXJjZS5wcm9wZXJ0eURpZmZlcmVuY2VzKSB7XG4gICAgICAgICAgY29uc29sZS5sb2coYCAgICAke2RpZmYucHJvcGVydHlQYXRofTpgKTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhgICAgICAgRXhwZWN0ZWQ6ICR7ZGlmZi5leHBlY3RlZFZhbHVlfWApO1xuICAgICAgICAgIGNvbnNvbGUubG9nKGAgICAgICBBY3R1YWw6ICR7ZGlmZi5hY3R1YWxWYWx1ZX1gKTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhgICAgICAgVHlwZTogJHtkaWZmLmRpZmZlcmVuY2VUeXBlfWApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBwcmludFN1bW1hcnkoKTogdm9pZCB7XG4gICAgY29uc29sZS5sb2coJ1xcbj09PT09PT09PT0gRFJJRlQgREVURUNUSU9OIFNVTU1BUlkgPT09PT09PT09PScpO1xuXG4gICAgY29uc3QgZHJpZnRlZFN0YWNrcyA9IHRoaXMucmVzdWx0cy5maWx0ZXIociA9PiByLmRyaWZ0U3RhdHVzID09PSAnRFJJRlRFRCcpO1xuICAgIGNvbnN0IHN5bmNlZFN0YWNrcyA9IHRoaXMucmVzdWx0cy5maWx0ZXIociA9PiByLmRyaWZ0U3RhdHVzID09PSAnSU5fU1lOQycpO1xuICAgIGNvbnN0IGVycm9yU3RhY2tzID0gdGhpcy5yZXN1bHRzLmZpbHRlcihyID0+IHIuZXJyb3IpO1xuXG4gICAgY29uc29sZS5sb2coYFRvdGFsIHN0YWNrcyBjaGVja2VkOiAke3RoaXMucmVzdWx0cy5sZW5ndGh9YCk7XG4gICAgY29uc29sZS5sb2coYEluIHN5bmM6ICR7c3luY2VkU3RhY2tzLmxlbmd0aH1gKTtcbiAgICBjb25zb2xlLmxvZyhgRHJpZnRlZDogJHtkcmlmdGVkU3RhY2tzLmxlbmd0aH1gKTtcbiAgICBjb25zb2xlLmxvZyhgRXJyb3JzOiAke2Vycm9yU3RhY2tzLmxlbmd0aH1gKTtcblxuICAgIGlmIChkcmlmdGVkU3RhY2tzLmxlbmd0aCA+IDApIHtcbiAgICAgIGNvbnNvbGUubG9nKCdcXG5EcmlmdGVkIHN0YWNrczonKTtcbiAgICAgIGZvciAoY29uc3Qgc3RhY2sgb2YgZHJpZnRlZFN0YWNrcykge1xuICAgICAgICBjb25zdCByZXNvdXJjZUNvdW50ID0gc3RhY2suZHJpZnRlZFJlc291cmNlcz8ubGVuZ3RoIHx8IDA7XG4gICAgICAgIGNvbnNvbGUubG9nKGAgIC0gJHtzdGFjay5zdGFja05hbWV9ICgke3Jlc291cmNlQ291bnR9IHJlc291cmNlcylgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoZXJyb3JTdGFja3MubGVuZ3RoID4gMCkge1xuICAgICAgY29uc29sZS5sb2coJ1xcblN0YWNrcyB3aXRoIGVycm9yczonKTtcbiAgICAgIGZvciAoY29uc3Qgc3RhY2sgb2YgZXJyb3JTdGFja3MpIHtcbiAgICAgICAgY29uc29sZS5sb2coYCAgLSAke3N0YWNrLnN0YWNrTmFtZX06ICR7c3RhY2suZXJyb3J9YCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzYXZlUmVzdWx0cygpOiB2b2lkIHtcbiAgICBjb25zdCBvdXRwdXRGaWxlID0gcHJvY2Vzcy5lbnYuRFJJRlRfREVURUNUSU9OX09VVFBVVCB8fCAnZHJpZnQtZGV0ZWN0aW9uLXJlc3VsdHMuanNvbic7XG4gICAgd3JpdGVGaWxlU3luYyhvdXRwdXRGaWxlLCBKU09OLnN0cmluZ2lmeSh0aGlzLnJlc3VsdHMsIG51bGwsIDIpKTtcbiAgICBjb25zb2xlLmxvZyhgXFxuUmVzdWx0cyBzYXZlZCB0bzogJHtvdXRwdXRGaWxlfWApO1xuICB9XG5cbiAgcHJpdmF0ZSBzaG91bGRGYWlsKCk6IGJvb2xlYW4ge1xuICAgIGlmICghdGhpcy5vcHRpb25zLmZhaWxPbkRyaWZ0KSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMucmVzdWx0cy5zb21lKHIgPT4gci5kcmlmdFN0YXR1cyA9PT0gJ0RSSUZURUQnKTtcbiAgfVxufVxuXG4vLyBQYXJzZSBjb21tYW5kIGxpbmUgYXJndW1lbnRzXG5mdW5jdGlvbiBwYXJzZUFyZ3MoKTogRHJpZnREZXRlY3Rpb25PcHRpb25zIHtcbiAgY29uc3QgYXJncyA9IHByb2Nlc3MuYXJndi5zbGljZSgyKTtcbiAgY29uc3Qgb3B0aW9uczogRHJpZnREZXRlY3Rpb25PcHRpb25zID0ge1xuICAgIHJlZ2lvbjogcHJvY2Vzcy5lbnYuQVdTX1JFR0lPTiB8fCAndXMtZWFzdC0xJyxcbiAgfTtcblxuICBmb3IgKGxldCBpID0gMDsgaSA8IGFyZ3MubGVuZ3RoOyBpKyspIHtcbiAgICBzd2l0Y2ggKGFyZ3NbaV0pIHtcbiAgICAgIGNhc2UgJy0tcmVnaW9uJzpcbiAgICAgICAgb3B0aW9ucy5yZWdpb24gPSBhcmdzWysraV07XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnLS1zdGFja3MnOlxuICAgICAgICBvcHRpb25zLnN0YWNrTmFtZXMgPSBhcmdzWysraV0uc3BsaXQoJywnKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICctLXRpbWVvdXQnOlxuICAgICAgICBvcHRpb25zLnRpbWVvdXQgPSBwYXJzZUludChhcmdzWysraV0pO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJy0tbm8tZmFpbC1vbi1kcmlmdCc6XG4gICAgICAgIG9wdGlvbnMuZmFpbE9uRHJpZnQgPSBmYWxzZTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICBjb25zb2xlLmVycm9yKGBVbmtub3duIGFyZ3VtZW50OiAke2FyZ3NbaV19YCk7XG4gICAgICAgIHByaW50VXNhZ2UoKTtcbiAgICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBvcHRpb25zO1xufVxuXG5mdW5jdGlvbiBwcmludFVzYWdlKCk6IHZvaWQge1xuICBjb25zb2xlLmxvZyhgXG5Vc2FnZTogZGV0ZWN0LWRyaWZ0LnRzIFtvcHRpb25zXVxuXG5PcHRpb25zOlxuICAtLXJlZ2lvbiA8cmVnaW9uPiAgICAgICAgICAgQVdTIHJlZ2lvbiAoZGVmYXVsdDogdXMtZWFzdC0xIG9yIEFXU19SRUdJT04gZW52IHZhcilcbiAgLS1zdGFja3MgPHN0YWNrMSxzdGFjazI+ICAgIENvbW1hLXNlcGFyYXRlZCBsaXN0IG9mIHN0YWNrIG5hbWVzIChkZWZhdWx0OiBhbGwgc3RhY2tzKVxuICAtLXRpbWVvdXQgPG1pbnV0ZXM+ICAgICAgICAgVGltZW91dCBpbiBtaW51dGVzIChkZWZhdWx0OiAzMClcbiAgLS1uby1mYWlsLW9uLWRyaWZ0ICAgICAgICAgRG9uJ3QgZXhpdCB3aXRoIGVycm9yIGNvZGUgaWYgZHJpZnQgaXMgZGV0ZWN0ZWRcblxuRW52aXJvbm1lbnQgdmFyaWFibGVzOlxuICBBV1NfUkVHSU9OICAgICAgICAgICAgICAgICAgRGVmYXVsdCBBV1MgcmVnaW9uXG4gIERSSUZUX0RFVEVDVElPTl9PVVRQVVQgICAgICBPdXRwdXQgZmlsZSBwYXRoIChkZWZhdWx0OiBkcmlmdC1kZXRlY3Rpb24tcmVzdWx0cy5qc29uKVxuYCk7XG59XG5cbi8vIE1haW4gZW50cnkgcG9pbnRcbmlmIChyZXF1aXJlLm1haW4gPT09IG1vZHVsZSkge1xuICBjb25zdCBvcHRpb25zID0gcGFyc2VBcmdzKCk7XG4gIGNvbnN0IGRldGVjdG9yID0gbmV3IERyaWZ0RGV0ZWN0b3Iob3B0aW9ucyk7XG4gIGRldGVjdG9yLnJ1bigpLmNhdGNoKGNvbnNvbGUuZXJyb3IpO1xufVxuXG5leHBvcnQgeyBEcmlmdERldGVjdG9yLCBEcmlmdERldGVjdGlvbk9wdGlvbnMsIERyaWZ0UmVzdWx0LCBLbm93bkVycm9yUmVzdWx0LCBEcmlmdGVkUmVzb3VyY2UgfTsiXX0=
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Project } from 'projen';
|
|
2
|
+
import { DriftDetectionWorkflow, DriftDetectionWorkflowOptions } from './base';
|
|
3
|
+
export interface GitHubDriftDetectionWorkflowOptions extends DriftDetectionWorkflowOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Additional permissions for GitHub workflow
|
|
6
|
+
*/
|
|
7
|
+
readonly permissions?: Record<string, string>;
|
|
8
|
+
/**
|
|
9
|
+
* Whether to create issues on drift detection
|
|
10
|
+
* @default false
|
|
11
|
+
*/
|
|
12
|
+
readonly createIssues?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare class GitHubDriftDetectionWorkflow extends DriftDetectionWorkflow {
|
|
15
|
+
private readonly permissions?;
|
|
16
|
+
private readonly createIssues;
|
|
17
|
+
private readonly workflow;
|
|
18
|
+
constructor(project: Project, options: GitHubDriftDetectionWorkflowOptions);
|
|
19
|
+
private generateIssueCreationScript;
|
|
20
|
+
private generateSummaryScript;
|
|
21
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.GitHubDriftDetectionWorkflow = void 0;
|
|
5
|
+
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
|
|
6
|
+
const workflows_model_1 = require("projen/lib/github/workflows-model");
|
|
7
|
+
const base_1 = require("./base");
|
|
8
|
+
const step_1 = require("./step");
|
|
9
|
+
class GitHubDriftDetectionWorkflow extends base_1.DriftDetectionWorkflow {
|
|
10
|
+
constructor(project, options) {
|
|
11
|
+
super(project, options);
|
|
12
|
+
this.permissions = options.permissions;
|
|
13
|
+
this.createIssues = options.createIssues ?? false;
|
|
14
|
+
this.workflow = this.project.github.addWorkflow('drift-detection');
|
|
15
|
+
this.workflow.on({
|
|
16
|
+
schedule: [{
|
|
17
|
+
cron: this.schedule,
|
|
18
|
+
}],
|
|
19
|
+
workflowDispatch: {
|
|
20
|
+
inputs: {
|
|
21
|
+
stage: {
|
|
22
|
+
description: 'Stage to check for drift (leave empty for all)',
|
|
23
|
+
required: false,
|
|
24
|
+
type: 'choice',
|
|
25
|
+
options: this.stages.map(s => s.name),
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
// Add job for each stage
|
|
31
|
+
for (const stage of this.stages) {
|
|
32
|
+
const jobId = `drift-${stage.name}`.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
33
|
+
const driftStep = new step_1.DriftDetectionStep(this.project, stage).toGithub();
|
|
34
|
+
this.workflow.addJob(jobId, {
|
|
35
|
+
name: `Drift Detection - ${stage.name}`,
|
|
36
|
+
runsOn: ['ubuntu-latest'],
|
|
37
|
+
if: `\${{ github.event_name == 'schedule' || github.event.inputs.stage == '' || github.event.inputs.stage == '${stage.name}' }}`,
|
|
38
|
+
env: driftStep.env,
|
|
39
|
+
permissions: {
|
|
40
|
+
contents: workflows_model_1.JobPermission.READ,
|
|
41
|
+
...(driftStep.permissions ?? {}),
|
|
42
|
+
...(this.createIssues ? { issues: workflows_model_1.JobPermission.WRITE } : {}),
|
|
43
|
+
...this.permissions,
|
|
44
|
+
},
|
|
45
|
+
steps: [
|
|
46
|
+
{
|
|
47
|
+
name: 'Checkout',
|
|
48
|
+
uses: 'actions/checkout@v4',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'Setup Node.js',
|
|
52
|
+
uses: 'actions/setup-node@v4',
|
|
53
|
+
with: {
|
|
54
|
+
'node-version': '20',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'Install dependencies',
|
|
59
|
+
run: 'npm ci',
|
|
60
|
+
},
|
|
61
|
+
...driftStep.steps,
|
|
62
|
+
{
|
|
63
|
+
name: 'Upload results',
|
|
64
|
+
uses: 'actions/upload-artifact@v4',
|
|
65
|
+
with: {
|
|
66
|
+
name: `drift-results-${stage.name}`,
|
|
67
|
+
path: `drift-results-${stage.name}.json`,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
...(this.createIssues ? [{
|
|
71
|
+
name: 'Create Issue on Drift',
|
|
72
|
+
if: 'steps.drift.outcome == \'failure\' && github.event_name == \'schedule\'',
|
|
73
|
+
uses: 'actions/github-script@v7',
|
|
74
|
+
with: {
|
|
75
|
+
script: this.generateIssueCreationScript(stage),
|
|
76
|
+
},
|
|
77
|
+
}] : []),
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Add summary job
|
|
82
|
+
if (this.stages.length > 0) {
|
|
83
|
+
this.workflow.addJob('drift-summary', {
|
|
84
|
+
name: 'Drift Detection Summary',
|
|
85
|
+
runsOn: ['ubuntu-latest'],
|
|
86
|
+
permissions: {
|
|
87
|
+
contents: workflows_model_1.JobPermission.READ,
|
|
88
|
+
},
|
|
89
|
+
needs: this.stages.map(stage => `drift-${stage.name}`),
|
|
90
|
+
steps: [
|
|
91
|
+
{
|
|
92
|
+
name: 'Download all artifacts',
|
|
93
|
+
uses: 'actions/download-artifact@v4',
|
|
94
|
+
with: {
|
|
95
|
+
path: 'drift-results',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'Generate summary',
|
|
100
|
+
run: this.generateSummaryScript(),
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
generateIssueCreationScript(stage) {
|
|
107
|
+
return `
|
|
108
|
+
const fs = require('fs');
|
|
109
|
+
const resultsFile = 'drift-results-${stage.name}.json';
|
|
110
|
+
|
|
111
|
+
if (!fs.existsSync(resultsFile)) {
|
|
112
|
+
console.log('No results file found');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const results = JSON.parse(fs.readFileSync(resultsFile, 'utf8'));
|
|
117
|
+
const driftedStacks = results.filter(r => r.driftStatus === 'DRIFTED');
|
|
118
|
+
|
|
119
|
+
if (driftedStacks.length === 0) {
|
|
120
|
+
console.log('No drift detected');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const title = 'Drift Detected in ${stage.name}';
|
|
125
|
+
const body = \`## Drift Detection Report
|
|
126
|
+
|
|
127
|
+
**Stage:** ${stage.name}
|
|
128
|
+
**Region:** ${stage.region}
|
|
129
|
+
**Time:** \${new Date().toISOString()}
|
|
130
|
+
|
|
131
|
+
### Summary
|
|
132
|
+
- Total stacks checked: \${results.length}
|
|
133
|
+
- Drifted stacks: \${driftedStacks.length}
|
|
134
|
+
|
|
135
|
+
### Drifted Stacks
|
|
136
|
+
\${driftedStacks.map(stack => {
|
|
137
|
+
const resources = stack.driftedResources || [];
|
|
138
|
+
return \`#### \${stack.stackName}
|
|
139
|
+
- Drifted resources: \${resources.length}
|
|
140
|
+
\${resources.map(r => \` - \${r.logicalResourceId} (\${r.resourceType})\`).join('\\n')}
|
|
141
|
+
\`;
|
|
142
|
+
}).join('\\n')}
|
|
143
|
+
|
|
144
|
+
### Action Required
|
|
145
|
+
Please review the drifted resources and either:
|
|
146
|
+
1. Update the infrastructure code to match the actual state
|
|
147
|
+
2. Restore the resources to match the expected state
|
|
148
|
+
|
|
149
|
+
[View workflow run](\${context.serverUrl}/\${context.repo.owner}/\${context.repo.repo}/actions/runs/\${context.runId})
|
|
150
|
+
\`;
|
|
151
|
+
|
|
152
|
+
// Check if issue already exists
|
|
153
|
+
const issues = await github.rest.issues.listForRepo({
|
|
154
|
+
owner: context.repo.owner,
|
|
155
|
+
repo: context.repo.repo,
|
|
156
|
+
state: 'open',
|
|
157
|
+
labels: ['drift-detection', '${stage.name}'],
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
if (issues.data.length === 0) {
|
|
161
|
+
await github.rest.issues.create({
|
|
162
|
+
owner: context.repo.owner,
|
|
163
|
+
repo: context.repo.repo,
|
|
164
|
+
title,
|
|
165
|
+
body,
|
|
166
|
+
labels: ['drift-detection', '${stage.name}'],
|
|
167
|
+
});
|
|
168
|
+
} else {
|
|
169
|
+
// Update existing issue
|
|
170
|
+
const issue = issues.data[0];
|
|
171
|
+
await github.rest.issues.createComment({
|
|
172
|
+
owner: context.repo.owner,
|
|
173
|
+
repo: context.repo.repo,
|
|
174
|
+
issue_number: issue.number,
|
|
175
|
+
body: body,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
`;
|
|
179
|
+
}
|
|
180
|
+
generateSummaryScript() {
|
|
181
|
+
return `
|
|
182
|
+
#!/bin/bash
|
|
183
|
+
echo "## Drift Detection Summary" >> $GITHUB_STEP_SUMMARY
|
|
184
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
185
|
+
|
|
186
|
+
total_stacks=0
|
|
187
|
+
total_drifted=0
|
|
188
|
+
total_errors=0
|
|
189
|
+
|
|
190
|
+
for file in drift-results-*.json; do
|
|
191
|
+
if [[ -f "$file" ]]; then
|
|
192
|
+
stage=$(basename $(dirname "$file"))
|
|
193
|
+
echo "### Stage: $stage" >> $GITHUB_STEP_SUMMARY
|
|
194
|
+
|
|
195
|
+
# Parse JSON and generate summary
|
|
196
|
+
jq -r '
|
|
197
|
+
. as $results |
|
|
198
|
+
"- Total stacks: " + ($results | length | tostring) + "\\n" +
|
|
199
|
+
"- Drifted: " + ([$results[] | select(.driftStatus == "DRIFTED")] | length | tostring) + "\\n" +
|
|
200
|
+
"- Errors: " + ([$results[] | select(.error)] | length | tostring) + "\\n" +
|
|
201
|
+
([$results[] | select(.driftStatus == "DRIFTED")] |
|
|
202
|
+
if length > 0 then
|
|
203
|
+
"\\n**Drifted stacks:**\\n" +
|
|
204
|
+
(map(" - " + .stackName + " (" + ((.driftedResources // []) | length | tostring) + " resources)") | join("\\n"))
|
|
205
|
+
else "" end)
|
|
206
|
+
' "$file" >> $GITHUB_STEP_SUMMARY
|
|
207
|
+
|
|
208
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
209
|
+
|
|
210
|
+
# Count totals
|
|
211
|
+
total_stacks=$((total_stacks + $(jq 'length' "$file")))
|
|
212
|
+
total_drifted=$((total_drifted + $(jq '[.[] | select(.driftStatus == "DRIFTED")] | length' "$file")))
|
|
213
|
+
total_errors=$((total_errors + $(jq '[.[] | select(.error)] | length' "$file")))
|
|
214
|
+
fi
|
|
215
|
+
done
|
|
216
|
+
|
|
217
|
+
echo "### Overall Summary" >> $GITHUB_STEP_SUMMARY
|
|
218
|
+
echo "- Total stacks checked: $total_stacks" >> $GITHUB_STEP_SUMMARY
|
|
219
|
+
echo "- Total drifted stacks: $total_drifted" >> $GITHUB_STEP_SUMMARY
|
|
220
|
+
echo "- Total errors: $total_errors" >> $GITHUB_STEP_SUMMARY
|
|
221
|
+
|
|
222
|
+
if [[ $total_drifted -gt 0 ]]; then
|
|
223
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
224
|
+
echo "⚠️ **Action required:** Drift detected in $total_drifted stacks" >> $GITHUB_STEP_SUMMARY
|
|
225
|
+
fi
|
|
226
|
+
`;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
exports.GitHubDriftDetectionWorkflow = GitHubDriftDetectionWorkflow;
|
|
230
|
+
_a = JSII_RTTI_SYMBOL_1;
|
|
231
|
+
GitHubDriftDetectionWorkflow[_a] = { fqn: "projen-pipelines.GitHubDriftDetectionWorkflow", version: "0.2.14" };
|
|
232
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2l0aHViLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2RyaWZ0L2dpdGh1Yi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUVBLHVFQUFrRTtBQUNsRSxpQ0FBMkc7QUFDM0csaUNBQTRDO0FBZTVDLE1BQWEsNEJBQTZCLFNBQVEsNkJBQXNCO0lBS3RFLFlBQVksT0FBZ0IsRUFBRSxPQUE0QztRQUN4RSxLQUFLLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3hCLElBQUksQ0FBQyxXQUFXLEdBQUcsT0FBTyxDQUFDLFdBQVcsQ0FBQztRQUN2QyxJQUFJLENBQUMsWUFBWSxHQUFHLE9BQU8sQ0FBQyxZQUFZLElBQUksS0FBSyxDQUFDO1FBRWxELElBQUksQ0FBQyxRQUFRLEdBQUksSUFBSSxDQUFDLE9BQXlCLENBQUMsTUFBTyxDQUFDLFdBQVcsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQ3ZGLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1lBQ2YsUUFBUSxFQUFFLENBQUM7b0JBQ1QsSUFBSSxFQUFFLElBQUksQ0FBQyxRQUFRO2lCQUNwQixDQUFDO1lBQ0YsZ0JBQWdCLEVBQUU7Z0JBQ2hCLE1BQU0sRUFBRTtvQkFDTixLQUFLLEVBQUU7d0JBQ0wsV0FBVyxFQUFFLGdEQUFnRDt3QkFDN0QsUUFBUSxFQUFFLEtBQUs7d0JBQ2YsSUFBSSxFQUFFLFFBQVE7d0JBQ2QsT0FBTyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztxQkFDdEM7aUJBQ0Y7YUFDRjtTQUNGLENBQUMsQ0FBQztRQUVILHlCQUF5QjtRQUN6QixLQUFLLE1BQU0sS0FBSyxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNoQyxNQUFNLEtBQUssR0FBRyxTQUFTLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FB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QztJQUNILENBQUM7SUFFTywyQkFBMkIsQ0FBQyxLQUFpQztRQUNuRSxPQUFPOztxQ0FFMEIsS0FBSyxDQUFDLElBQUk7Ozs7Ozs7Ozs7Ozs7OzttQ0FlWixLQUFLLENBQUMsSUFBSTs7O2FBR2hDLEtBQUssQ0FBQyxJQUFJO2NBQ1QsS0FBSyxDQUFDLE1BQU07Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O2lDQTZCTyxLQUFLLENBQUMsSUFBSTs7Ozs7Ozs7O21DQVNSLEtBQUssQ0FBQyxJQUFJOzs7Ozs7Ozs7Ozs7Q0FZNUMsQ0FBQztJQUNBLENBQUM7SUFFTyxxQkFBcUI7UUFDM0IsT0FBTzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0NBNkNWLENBQUM7SUFDQSxDQUFDOztBQXJPSCxvRUF1T0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBQcm9qZWN0IH0gZnJvbSAncHJvamVuJztcbmltcG9ydCB7IEdpdEh1YlByb2plY3QsIEdpdGh1YldvcmtmbG93IH0gZnJvbSAncHJvamVuL2xpYi9naXRodWInO1xuaW1wb3J0IHsgSm9iUGVybWlzc2lvbiB9IGZyb20gJ3Byb2plbi9saWIvZ2l0aHViL3dvcmtmbG93cy1tb2RlbCc7XG5pbXBvcnQgeyBEcmlmdERldGVjdGlvbldvcmtmbG93LCBEcmlmdERldGVjdGlvbldvcmtmbG93T3B0aW9ucywgRHJpZnREZXRlY3Rpb25TdGFnZU9wdGlvbnMgfSBmcm9tICcuL2Jhc2UnO1xuaW1wb3J0IHsgRHJpZnREZXRlY3Rpb25TdGVwIH0gZnJvbSAnLi9zdGVwJztcblxuZXhwb3J0IGludGVyZmFjZSBHaXRIdWJEcmlmdERldGVjdGlvbldvcmtmbG93T3B0aW9ucyBleHRlbmRzIERyaWZ0RGV0ZWN0aW9uV29ya2Zsb3dPcHRpb25zIHtcbiAgLyoqXG4gICAqIEFkZGl0aW9uYWwgcGVybWlzc2lvbnMgZm9yIEdpdEh1YiB3b3JrZmxvd1xuICAgKi9cbiAgcmVhZG9ubHkgcGVybWlzc2lvbnM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRvIGNyZWF0ZSBpc3N1ZXMgb24gZHJpZnQgZGV0ZWN0aW9uXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICByZWFkb25seSBjcmVhdGVJc3N1ZXM/OiBib29sZWFuO1xufVxuXG5leHBvcnQgY2xhc3MgR2l0SHViRHJpZnREZXRlY3Rpb25Xb3JrZmxvdyBleHRlbmRzIERyaWZ0RGV0ZWN0aW9uV29ya2Zsb3cge1xuICBwcml2YXRlIHJlYWRvbmx5IHBlcm1pc3Npb25zPzogUmVjb3JkPHN0cmluZywgc3RyaW5nPjtcbiAgcHJpdmF0ZSByZWFkb25seSBjcmVhdGVJc3N1ZXM6IGJvb2xlYW47XG4gIHByaXZhdGUgcmVhZG9ubHkgd29ya2Zsb3c6IEdpdGh1YldvcmtmbG93O1xuXG4gIGNvbnN0cnVjdG9yKHByb2plY3Q6IFByb2plY3QsIG9wdGlvbnM6IEdpdEh1YkRyaWZ0RGV0ZWN0aW9uV29ya2Zsb3dPcHRpb25zKSB7XG4gICAgc3VwZXIocHJvamVjdCwgb3B0aW9ucyk7XG4gICAgdGhpcy5wZXJtaXNzaW9ucyA9IG9wdGlvbnMucGVybWlzc2lvbnM7XG4gICAgdGhpcy5jcmVhdGVJc3N1ZXMgPSBvcHRpb25zLmNyZWF0ZUlzc3VlcyA/PyBmYWxzZTtcblxuICAgIHRoaXMud29ya2Zsb3cgPSAodGhpcy5wcm9qZWN0IGFzIEdpdEh1YlByb2plY3QpLmdpdGh1YiEuYWRkV29ya2Zsb3coJ2RyaWZ0LWRldGVjdGlvbicpO1xuICAgIHRoaXMud29ya2Zsb3cub24oe1xuICAgICAgc2NoZWR1bGU6IFt7XG4gICAgICAgIGNyb246IHRoaXMuc2NoZWR1bGUsXG4gICAgICB9XSxcbiAgICAgIHdvcmtmbG93RGlzcGF0Y2g6IHtcbiAgICAgICAgaW5wdXRzOiB7XG4gICAgICAgICAgc3RhZ2U6IHtcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uOiAnU3RhZ2UgdG8gY2hlY2sgZm9yIGRyaWZ0IChsZWF2ZSBlbXB0eSBmb3IgYWxsKScsXG4gICAgICAgICAgICByZXF1aXJlZDogZmFsc2UsXG4gICAgICAgICAgICB0eXBlOiAnY2hvaWNlJyxcbiAgICAgICAgICAgIG9wdGlvbnM6IHRoaXMuc3RhZ2VzLm1hcChzID0+IHMubmFtZSksXG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICAvLyBBZGQgam9iIGZvciBlYWNoIHN0YWdlXG4gICAgZm9yIChjb25zdCBzdGFnZSBvZiB0aGlzLnN0YWdlcykge1xuICAgICAgY29uc3Qgam9iSWQgPSBgZHJpZnQtJHtzdGFnZS5uYW1lfWAudG9Mb3dlckNhc2UoKS5yZXBsYWNlKC9bXmEtejAtOS1dL2csICctJyk7XG5cbiAgICAgIGNvbnN0IGRyaWZ0U3RlcCA9IG5ldyBEcmlmdERldGVjdGlvblN0ZXAodGhpcy5wcm9qZWN0LCBzdGFnZSkudG9HaXRodWIoKTtcblxuICAgICAgdGhpcy53b3JrZmxvdy5hZGRKb2Ioam9iSWQsIHtcbiAgICAgICAgbmFtZTogYERyaWZ0IERldGVjdGlvbiAtICR7c3RhZ2UubmFtZX1gLFxuICAgICAgICBydW5zT246IFsndWJ1bnR1LWxhdGVzdCddLFxuICAgICAgICBpZjogYFxcJHt7IGdpdGh1Yi5ldmVudF9uYW1lID09ICdzY2hlZHVsZScgfHwgZ2l0aHViLmV2ZW50LmlucHV0cy5zdGFnZSA9PSAnJyB8fCBnaXRodWIuZXZlbnQuaW5wdXRzLnN0YWdlID09ICcke3N0YWdlLm5hbWV9JyB9fWAsXG4gICAgICAgIGVudjogZHJpZnRTdGVwLmVudixcbiAgICAgICAgcGVybWlzc2lvbnM6IHtcbiAgICAgICAgICBjb250ZW50czogSm9iUGVybWlzc2lvbi5SRUFELFxuICAgICAgICAgIC4uLihkcmlmdFN0ZXAucGVybWlzc2lvbnMgPz8ge30pLFxuICAgICAgICAgIC4uLih0aGlzLmNyZWF0ZUlzc3VlcyA/IHsgaXNzdWVzOiBKb2JQZXJtaXNzaW9uLldSSVRFIH0gOiB7fSksXG4gICAgICAgICAgLi4udGhpcy5wZXJtaXNzaW9ucyxcbiAgICAgICAgfSxcbiAgICAgICAgc3RlcHM6IFtcbiAgICAgICAgICB7XG4gICAgICAgICAgICBuYW1lOiAnQ2hlY2tvdXQnLFxuICAgICAgICAgICAgdXNlczogJ2FjdGlvbnMvY2hlY2tvdXRAdjQnLFxuICAgICAgICAgIH0sXG4gICAgICAgICAge1xuICAgICAgICAgICAgbmFtZTogJ1NldHVwIE5vZGUuanMnLFxuICAgICAgICAgICAgdXNlczogJ2FjdGlvbnMvc2V0dXAtbm9kZUB2NCcsXG4gICAgICAgICAgICB3aXRoOiB7XG4gICAgICAgICAgICAgICdub2RlLXZlcnNpb24nOiAnMjAnLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIG5hbWU6ICdJbnN0YWxsIGRlcGVuZGVuY2llcycsXG4gICAgICAgICAgICBydW46ICducG0gY2knLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgLi4uZHJpZnRTdGVwLnN0ZXBzLFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIG5hbWU6ICdVcGxvYWQgcmVzdWx0cycsXG4gICAgICAgICAgICB1c2VzOiAnYWN0aW9ucy91cGxvYWQtYXJ0aWZhY3RAdjQnLFxuICAgICAgICAgICAgd2l0aDoge1xuICAgICAgICAgICAgICBuYW1lOiBgZHJpZnQtcmVzdWx0cy0ke3N0YWdlLm5hbWV9YCxcbiAgICAgICAgICAgICAgcGF0aDogYGRyaWZ0LXJlc3VsdHMtJHtzdGFnZS5uYW1lfS5qc29uYCxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSxcbiAgICAgICAgICAuLi4odGhpcy5jcmVhdGVJc3N1ZXMgPyBbe1xuICAgICAgICAgICAgbmFtZTogJ0NyZWF0ZSBJc3N1ZSBvbiBEcmlmdCcsXG4gICAgICAgICAgICBpZjogJ3N0ZXBzLmRyaWZ0Lm91dGNvbWUgPT0gXFwnZmFpbHVyZVxcJyAmJiBnaXRodWIuZXZlbnRfbmFtZSA9PSBcXCdzY2hlZHVsZVxcJycsXG4gICAgICAgICAgICB1c2VzOiAnYWN0aW9ucy9naXRodWItc2NyaXB0QHY3JyxcbiAgICAgICAgICAgIHdpdGg6IHtcbiAgICAgICAgICAgICAgc2NyaXB0OiB0aGlzLmdlbmVyYXRlSXNzdWVDcmVhdGlvblNjcmlwdChzdGFnZSksXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH1dIDogW10pLFxuICAgICAgICBdLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gQWRkIHN1bW1hcnkgam9iXG4gICAgaWYgKHRoaXMuc3RhZ2VzLmxlbmd0aCA+IDApIHtcbiAgICAgIHRoaXMud29ya2Zsb3cuYWRkSm9iKCdkcmlmdC1zdW1tYXJ5Jywge1xuICAgICAgICBuYW1lOiAnRHJpZnQgRGV0ZWN0aW9uIFN1bW1hcnknLFxuICAgICAgICBydW5zT246IFsndWJ1bnR1LWxhdGVzdCddLFxuICAgICAgICBwZXJtaXNzaW9uczoge1xuICAgICAgICAgIGNvbnRlbnRzOiBKb2JQZXJtaXNzaW9uLlJFQUQsXG4gICAgICAgIH0sXG4gICAgICAgIG5lZWRzOiB0aGlzLnN0YWdlcy5tYXAoc3RhZ2UgPT4gYGRyaWZ0LSR7c3RhZ2UubmFtZX1gKSxcbiAgICAgICAgc3RlcHM6IFtcbiAgICAgICAgICB7XG4gICAgICAgICAgICBuYW1lOiAnRG93bmxvYWQgYWxsIGFydGlmYWN0cycsXG4gICAgICAgICAgICB1c2VzOiAnYWN0aW9ucy9kb3dubG9hZC1hcnRpZmFjdEB2NCcsXG4gICAgICAgICAgICB3aXRoOiB7XG4gICAgICAgICAgICAgIHBhdGg6ICdkcmlmdC1yZXN1bHRzJyxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSxcbiAgICAgICAgICB7XG4gICAgICAgICAgICBuYW1lOiAnR2VuZXJhdGUgc3VtbWFyeScsXG4gICAgICAgICAgICBydW46IHRoaXMuZ2VuZXJhdGVTdW1tYXJ5U2NyaXB0KCksXG4gICAgICAgICAgfSxcbiAgICAgICAgXSxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZ2VuZXJhdGVJc3N1ZUNyZWF0aW9uU2NyaXB0KHN0YWdlOiBEcmlmdERldGVjdGlvblN0YWdlT3B0aW9ucyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGBcbmNvbnN0IGZzID0gcmVxdWlyZSgnZnMnKTtcbmNvbnN0IHJlc3VsdHNGaWxlID0gJ2RyaWZ0LXJlc3VsdHMtJHtzdGFnZS5uYW1lfS5qc29uJztcblxuaWYgKCFmcy5leGlzdHNTeW5jKHJlc3VsdHNGaWxlKSkge1xuICBjb25zb2xlLmxvZygnTm8gcmVzdWx0cyBmaWxlIGZvdW5kJyk7XG4gIHJldHVybjtcbn1cblxuY29uc3QgcmVzdWx0cyA9IEpTT04ucGFyc2UoZnMucmVhZEZpbGVTeW5jKHJlc3VsdHNGaWxlLCAndXRmOCcpKTtcbmNvbnN0IGRyaWZ0ZWRTdGFja3MgPSByZXN1bHRzLmZpbHRlcihyID0+IHIuZHJpZnRTdGF0dXMgPT09ICdEUklGVEVEJyk7XG5cbmlmIChkcmlmdGVkU3RhY2tzLmxlbmd0aCA9PT0gMCkge1xuICBjb25zb2xlLmxvZygnTm8gZHJpZnQgZGV0ZWN0ZWQnKTtcbiAgcmV0dXJuO1xufVxuXG5jb25zdCB0aXRsZSA9ICdEcmlmdCBEZXRlY3RlZCBpbiAke3N0YWdlLm5hbWV9JztcbmNvbnN0IGJvZHkgPSBcXGAjIyBEcmlmdCBEZXRlY3Rpb24gUmVwb3J0XG5cbioqU3RhZ2U6KiogJHtzdGFnZS5uYW1lfVxuKipSZWdpb246KiogJHtzdGFnZS5yZWdpb259XG4qKlRpbWU6KiogXFwke25ldyBEYXRlKCkudG9JU09TdHJpbmcoKX1cblxuIyMjIFN1bW1hcnlcbi0gVG90YWwgc3RhY2tzIGNoZWNrZWQ6IFxcJHtyZXN1bHRzLmxlbmd0aH1cbi0gRHJpZnRlZCBzdGFja3M6IFxcJHtkcmlmdGVkU3RhY2tzLmxlbmd0aH1cblxuIyMjIERyaWZ0ZWQgU3RhY2tzXG5cXCR7ZHJpZnRlZFN0YWNrcy5tYXAoc3RhY2sgPT4ge1xuICBjb25zdCByZXNvdXJjZXMgPSBzdGFjay5kcmlmdGVkUmVzb3VyY2VzIHx8IFtdO1xuICByZXR1cm4gXFxgIyMjIyBcXCR7c3RhY2suc3RhY2tOYW1lfVxuLSBEcmlmdGVkIHJlc291cmNlczogXFwke3Jlc291cmNlcy5sZW5ndGh9XG5cXCR7cmVzb3VyY2VzLm1hcChyID0+IFxcYCAgLSBcXCR7ci5sb2dpY2FsUmVzb3VyY2VJZH0gKFxcJHtyLnJlc291cmNlVHlwZX0pXFxgKS5qb2luKCdcXFxcbicpfVxuXFxgO1xufSkuam9pbignXFxcXG4nKX1cblxuIyMjIEFjdGlvbiBSZXF1aXJlZFxuUGxlYXNlIHJldmlldyB0aGUgZHJpZnRlZCByZXNvdXJjZXMgYW5kIGVpdGhlcjpcbjEuIFVwZGF0ZSB0aGUgaW5mcmFzdHJ1Y3R1cmUgY29kZSB0byBtYXRjaCB0aGUgYWN0dWFsIHN0YXRlXG4yLiBSZXN0b3JlIHRoZSByZXNvdXJjZXMgdG8gbWF0Y2ggdGhlIGV4cGVjdGVkIHN0YXRlXG5cbltWaWV3IHdvcmtmbG93IHJ1bl0oXFwke2NvbnRleHQuc2VydmVyVXJsfS9cXCR7Y29udGV4dC5yZXBvLm93bmVyfS9cXCR7Y29udGV4dC5yZXBvLnJlcG99L2FjdGlvbnMvcnVucy9cXCR7Y29udGV4dC5ydW5JZH0pXG5cXGA7XG5cbi8vIENoZWNrIGlmIGlzc3VlIGFscmVhZHkgZXhpc3RzXG5jb25zdCBpc3N1ZXMgPSBhd2FpdCBnaXRodWIucmVzdC5pc3N1ZXMubGlzdEZvclJlcG8oe1xuICBvd25lcjogY29udGV4dC5yZXBvLm93bmVyLFxuICByZXBvOiBjb250ZXh0LnJlcG8ucmVwbyxcbiAgc3RhdGU6ICdvcGVuJyxcbiAgbGFiZWxzOiBbJ2RyaWZ0LWRldGVjdGlvbicsICcke3N0YWdlLm5hbWV9J10sXG59KTtcblxuaWYgKGlzc3Vlcy5kYXRhLmxlbmd0aCA9PT0gMCkge1xuICBhd2FpdCBnaXRodWIucmVzdC5pc3N1ZXMuY3JlYXRlKHtcbiAgICBvd25lcjogY29udGV4dC5yZXBvLm93bmVyLFxuICAgIHJlcG86IGNvbnRleHQucmVwby5yZXBvLFxuICAgIHRpdGxlLFxuICAgIGJvZHksXG4gICAgbGFiZWxzOiBbJ2RyaWZ0LWRldGVjdGlvbicsICcke3N0YWdlLm5hbWV9J10sXG4gIH0pO1xufSBlbHNlIHtcbiAgLy8gVXBkYXRlIGV4aXN0aW5nIGlzc3VlXG4gIGNvbnN0IGlzc3VlID0gaXNzdWVzLmRhdGFbMF07XG4gIGF3YWl0IGdpdGh1Yi5yZXN0Lmlzc3Vlcy5jcmVhdGVDb21tZW50KHtcbiAgICBvd25lcjogY29udGV4dC5yZXBvLm93bmVyLFxuICAgIHJlcG86IGNvbnRleHQucmVwby5yZXBvLFxuICAgIGlzc3VlX251bWJlcjogaXNzdWUubnVtYmVyLFxuICAgIGJvZHk6IGJvZHksXG4gIH0pO1xufVxuYDtcbiAgfVxuXG4gIHByaXZhdGUgZ2VuZXJhdGVTdW1tYXJ5U2NyaXB0KCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGBcbiMhL2Jpbi9iYXNoXG5lY2hvIFwiIyMgRHJpZnQgRGV0ZWN0aW9uIFN1bW1hcnlcIiA+PiAkR0lUSFVCX1NURVBfU1VNTUFSWVxuZWNobyBcIlwiID4+ICRHSVRIVUJfU1RFUF9TVU1NQVJZXG5cbnRvdGFsX3N0YWNrcz0wXG50b3RhbF9kcmlmdGVkPTBcbnRvdGFsX2Vycm9ycz0wXG5cbmZvciBmaWxlIGluIGRyaWZ0LXJlc3VsdHMtKi5qc29uOyBkb1xuICBpZiBbWyAtZiBcIiRmaWxlXCIgXV07IHRoZW5cbiAgICBzdGFnZT0kKGJhc2VuYW1lICQoZGlybmFtZSBcIiRmaWxlXCIpKVxuICAgIGVjaG8gXCIjIyMgU3RhZ2U6ICRzdGFnZVwiID4+ICRHSVRIVUJfU1RFUF9TVU1NQVJZXG4gICAgXG4gICAgIyBQYXJzZSBKU09OIGFuZCBnZW5lcmF0ZSBzdW1tYXJ5XG4gICAganEgLXIgJ1xuICAgICAgLiBhcyAkcmVzdWx0cyB8XG4gICAgICBcIi0gVG90YWwgc3RhY2tzOiBcIiArICgkcmVzdWx0cyB8IGxlbmd0aCB8IHRvc3RyaW5nKSArIFwiXFxcXG5cIiArXG4gICAgICBcIi0gRHJpZnRlZDogXCIgKyAoWyRyZXN1bHRzW10gfCBzZWxlY3QoLmRyaWZ0U3RhdHVzID09IFwiRFJJRlRFRFwiKV0gfCBsZW5ndGggfCB0b3N0cmluZykgKyBcIlxcXFxuXCIgK1xuICAgICAgXCItIEVycm9yczogXCIgKyAoWyRyZXN1bHRzW10gfCBzZWxlY3QoLmVycm9yKV0gfCBsZW5ndGggfCB0b3N0cmluZykgKyBcIlxcXFxuXCIgK1xuICAgICAgKFskcmVzdWx0c1tdIHwgc2VsZWN0KC5kcmlmdFN0YXR1cyA9PSBcIkRSSUZURURcIildIHwgXG4gICAgICAgIGlmIGxlbmd0aCA+IDAgdGhlblxuICAgICAgICAgIFwiXFxcXG4qKkRyaWZ0ZWQgc3RhY2tzOioqXFxcXG5cIiArIFxuICAgICAgICAgIChtYXAoXCIgIC0gXCIgKyAuc3RhY2tOYW1lICsgXCIgKFwiICsgKCguZHJpZnRlZFJlc291cmNlcyAvLyBbXSkgfCBsZW5ndGggfCB0b3N0cmluZykgKyBcIiByZXNvdXJjZXMpXCIpIHwgam9pbihcIlxcXFxuXCIpKVxuICAgICAgICBlbHNlIFwiXCIgZW5kKVxuICAgICcgXCIkZmlsZVwiID4+ICRHSVRIVUJfU1RFUF9TVU1NQVJZXG4gICAgXG4gICAgZWNobyBcIlwiID4+ICRHSVRIVUJfU1RFUF9TVU1NQVJZXG4gICAgXG4gICAgIyBDb3VudCB0b3RhbHNcbiAgICB0b3RhbF9zdGFja3M9JCgodG90YWxfc3RhY2tzICsgJChqcSAnbGVuZ3RoJyBcIiRmaWxlXCIpKSlcbiAgICB0b3RhbF9kcmlmdGVkPSQoKHRvdGFsX2RyaWZ0ZWQgKyAkKGpxICdbLltdIHwgc2VsZWN0KC5kcmlmdFN0YXR1cyA9PSBcIkRSSUZURURcIildIHwgbGVuZ3RoJyBcIiRmaWxlXCIpKSlcbiAgICB0b3RhbF9lcnJvcnM9JCgodG90YWxfZXJyb3JzICsgJChqcSAnWy5bXSB8IHNlbGVjdCguZXJyb3IpXSB8IGxlbmd0aCcgXCIkZmlsZVwiKSkpXG4gIGZpXG5kb25lXG5cbmVjaG8gXCIjIyMgT3ZlcmFsbCBTdW1tYXJ5XCIgPj4gJEdJVEhVQl9TVEVQX1NVTU1BUllcbmVjaG8gXCItIFRvdGFsIHN0YWNrcyBjaGVja2VkOiAkdG90YWxfc3RhY2tzXCIgPj4gJEdJVEhVQl9TVEVQX1NVTU1BUllcbmVjaG8gXCItIFRvdGFsIGRyaWZ0ZWQgc3RhY2tzOiAkdG90YWxfZHJpZnRlZFwiID4+ICRHSVRIVUJfU1RFUF9TVU1NQVJZXG5lY2hvIFwiLSBUb3RhbCBlcnJvcnM6ICR0b3RhbF9lcnJvcnNcIiA+PiAkR0lUSFVCX1NURVBfU1VNTUFSWVxuXG5pZiBbWyAkdG90YWxfZHJpZnRlZCAtZ3QgMCBdXTsgdGhlblxuICBlY2hvIFwiXCIgPj4gJEdJVEhVQl9TVEVQX1NVTU1BUllcbiAgZWNobyBcIuKaoO+4jyAqKkFjdGlvbiByZXF1aXJlZDoqKiBEcmlmdCBkZXRlY3RlZCBpbiAkdG90YWxfZHJpZnRlZCBzdGFja3NcIiA+PiAkR0lUSFVCX1NURVBfU1VNTUFSWVxuZmlcbmA7XG4gIH1cblxufSJdfQ==
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Project } from 'projen';
|
|
2
|
+
import { DriftDetectionWorkflow, DriftDetectionWorkflowOptions } from './base';
|
|
3
|
+
export interface GitLabDriftDetectionWorkflowOptions extends DriftDetectionWorkflowOptions {
|
|
4
|
+
/**
|
|
5
|
+
* GitLab runner tags
|
|
6
|
+
*/
|
|
7
|
+
readonly runnerTags?: string[];
|
|
8
|
+
/**
|
|
9
|
+
* Docker image to use for drift detection
|
|
10
|
+
* @default "node:18"
|
|
11
|
+
*/
|
|
12
|
+
readonly image?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class GitLabDriftDetectionWorkflow extends DriftDetectionWorkflow {
|
|
15
|
+
private readonly runnerTags;
|
|
16
|
+
private readonly image;
|
|
17
|
+
private readonly config;
|
|
18
|
+
constructor(project: Project, options: GitLabDriftDetectionWorkflowOptions);
|
|
19
|
+
private generateSummaryScript;
|
|
20
|
+
}
|