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.
Files changed (49) hide show
  1. package/.jsii +5703 -1380
  2. package/API.md +5813 -1492
  3. package/README.md +253 -0
  4. package/docs/drift-detection.md +264 -0
  5. package/lib/assign-approver/base.js +1 -1
  6. package/lib/assign-approver/github.js +1 -1
  7. package/lib/awscdk/base.d.ts +21 -0
  8. package/lib/awscdk/base.js +246 -2
  9. package/lib/awscdk/bash.js +1 -1
  10. package/lib/awscdk/github.js +1 -1
  11. package/lib/awscdk/gitlab.js +1 -1
  12. package/lib/drift/base.d.ts +64 -0
  13. package/lib/drift/base.js +18 -0
  14. package/lib/drift/bash.d.ts +15 -0
  15. package/lib/drift/bash.js +170 -0
  16. package/lib/drift/detect-drift.d.ts +54 -0
  17. package/lib/drift/detect-drift.js +259 -0
  18. package/lib/drift/github.d.ts +21 -0
  19. package/lib/drift/github.js +232 -0
  20. package/lib/drift/gitlab.d.ts +20 -0
  21. package/lib/drift/gitlab.js +138 -0
  22. package/lib/drift/index.d.ts +5 -0
  23. package/lib/drift/index.js +22 -0
  24. package/lib/drift/step.d.ts +14 -0
  25. package/lib/drift/step.js +48 -0
  26. package/lib/index.d.ts +2 -0
  27. package/lib/index.js +3 -1
  28. package/lib/steps/artifact-steps.js +2 -2
  29. package/lib/steps/aws-assume-role.step.js +1 -1
  30. package/lib/steps/registries.js +2 -2
  31. package/lib/steps/step.d.ts +6 -1
  32. package/lib/steps/step.js +14 -10
  33. package/lib/versioning/computation.d.ts +63 -0
  34. package/lib/versioning/computation.js +121 -0
  35. package/lib/versioning/config.d.ts +41 -0
  36. package/lib/versioning/config.js +91 -0
  37. package/lib/versioning/index.d.ts +7 -0
  38. package/lib/versioning/index.js +46 -0
  39. package/lib/versioning/outputs.d.ts +87 -0
  40. package/lib/versioning/outputs.js +166 -0
  41. package/lib/versioning/setup.d.ts +30 -0
  42. package/lib/versioning/setup.js +165 -0
  43. package/lib/versioning/strategy.d.ts +21 -0
  44. package/lib/versioning/strategy.js +51 -0
  45. package/lib/versioning/types.d.ts +183 -0
  46. package/lib/versioning/types.js +3 -0
  47. package/lib/versioning/version-info.d.ts +106 -0
  48. package/lib/versioning/version-info.js +269 -0
  49. 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
+ }