@aws-cdk-testing/cli-integ 2.61.0
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/LICENSE +201 -0
- package/NOTICE +16 -0
- package/README.md +151 -0
- package/bin/apply-patches +19 -0
- package/bin/download-and-run-old-tests +52 -0
- package/bin/query-github +2 -0
- package/bin/query-github.d.ts +1 -0
- package/bin/query-github.js +55 -0
- package/bin/run-suite +2 -0
- package/bin/run-suite.d.ts +1 -0
- package/bin/run-suite.js +126 -0
- package/bin/stage-distribution +2 -0
- package/bin/stage-distribution.d.ts +1 -0
- package/bin/stage-distribution.js +209 -0
- package/bin/test-root +2 -0
- package/bin/test-root.d.ts +1 -0
- package/bin/test-root.js +6 -0
- package/entrypoints/test-cli-regression-against-current-code.sh +11 -0
- package/entrypoints/test-cli-regression-against-latest-release.sh +11 -0
- package/entrypoints/test-cli-regression.bash +83 -0
- package/entrypoints/test.sh +12 -0
- package/lib/aws.d.ts +55 -0
- package/lib/aws.js +243 -0
- package/lib/corking.d.ts +13 -0
- package/lib/corking.js +34 -0
- package/lib/files.d.ts +15 -0
- package/lib/files.js +80 -0
- package/lib/github.d.ts +4 -0
- package/lib/github.js +43 -0
- package/lib/index.d.ts +12 -0
- package/lib/index.js +25 -0
- package/lib/integ-test.d.ts +11 -0
- package/lib/integ-test.js +55 -0
- package/lib/lists.d.ts +1 -0
- package/lib/lists.js +12 -0
- package/lib/memoize.d.ts +6 -0
- package/lib/memoize.js +19 -0
- package/lib/npm.d.ts +4 -0
- package/lib/npm.js +15 -0
- package/lib/package-sources/release-source.d.ts +22 -0
- package/lib/package-sources/release-source.js +67 -0
- package/lib/package-sources/repo-source.d.ts +23 -0
- package/lib/package-sources/repo-source.js +92 -0
- package/lib/package-sources/repo-tools/npm +2 -0
- package/lib/package-sources/repo-tools/npm.d.ts +1 -0
- package/lib/package-sources/repo-tools/npm.js +42 -0
- package/lib/package-sources/source.d.ts +24 -0
- package/lib/package-sources/source.js +3 -0
- package/lib/package-sources/subprocess.d.ts +3 -0
- package/lib/package-sources/subprocess.js +18 -0
- package/lib/resource-pool.d.ts +54 -0
- package/lib/resource-pool.js +120 -0
- package/lib/resources.d.ts +1 -0
- package/lib/resources.js +6 -0
- package/lib/shell.d.ts +59 -0
- package/lib/shell.js +118 -0
- package/lib/staging/codeartifact.d.ts +44 -0
- package/lib/staging/codeartifact.js +258 -0
- package/lib/staging/maven.d.ts +5 -0
- package/lib/staging/maven.js +83 -0
- package/lib/staging/npm.d.ts +4 -0
- package/lib/staging/npm.js +56 -0
- package/lib/staging/nuget.d.ts +4 -0
- package/lib/staging/nuget.js +71 -0
- package/lib/staging/parallel-shell.d.ts +6 -0
- package/lib/staging/parallel-shell.js +46 -0
- package/lib/staging/pypi.d.ts +4 -0
- package/lib/staging/pypi.js +49 -0
- package/lib/staging/usage-dir.d.ts +31 -0
- package/lib/staging/usage-dir.js +87 -0
- package/lib/with-aws.d.ts +13 -0
- package/lib/with-aws.js +60 -0
- package/lib/with-cdk-app.d.ts +146 -0
- package/lib/with-cdk-app.js +398 -0
- package/lib/with-packages.d.ts +5 -0
- package/lib/with-packages.js +14 -0
- package/lib/with-sam.d.ts +33 -0
- package/lib/with-sam.js +240 -0
- package/lib/with-temporary-directory.d.ts +5 -0
- package/lib/with-temporary-directory.js +32 -0
- package/lib/xpmutex.d.ts +43 -0
- package/lib/xpmutex.js +207 -0
- package/package.json +73 -0
- package/resources/cdk-apps/app/app.js +463 -0
- package/resources/cdk-apps/app/cdk.json +7 -0
- package/resources/cdk-apps/app/docker/Dockerfile +2 -0
- package/resources/cdk-apps/app/docker/Dockerfile.Custom +2 -0
- package/resources/cdk-apps/app/lambda/index.js +4 -0
- package/resources/cdk-apps/app/lambda/response.json +3 -0
- package/resources/cdk-apps/app/nested-stack.js +49 -0
- package/resources/cdk-apps/cfn-include-app/.gitignore +1 -0
- package/resources/cdk-apps/cfn-include-app/cdk.json +4 -0
- package/resources/cdk-apps/cfn-include-app/cfn-include-app.js +21 -0
- package/resources/cdk-apps/cfn-include-app/example-template.json +13 -0
- package/resources/cdk-apps/sam_cdk_integ_app/bin/test-app.js +11 -0
- package/resources/cdk-apps/sam_cdk_integ_app/cdk.json +6 -0
- package/resources/cdk-apps/sam_cdk_integ_app/lib/nested-stack.js +19 -0
- package/resources/cdk-apps/sam_cdk_integ_app/lib/test-stack.js +134 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/docker/DockerImageFunctionConstruct/.no-packagejson-validator +0 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/docker/DockerImageFunctionConstruct/Dockerfile +9 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/docker/DockerImageFunctionConstruct/app.js +22 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/docker/DockerImageFunctionConstruct/package.json +18 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/go/GoFunctionConstruct/go.mod +5 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/go/GoFunctionConstruct/go.sum +17 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/go/GoFunctionConstruct/main.go +17 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/nodejs/NodeJsFunctionConstruct/.no-packagejson-validator +0 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/nodejs/NodeJsFunctionConstruct/app.ts +16 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/nodejs/NodeJsFunctionConstruct/package-lock.json +12 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/nodejs/NodeJsFunctionConstruct/package.json +5 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/python/Function/app.py +15 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/python/Function/requirements.txt +1 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/python/Layer/layer_version_dependency.py +5 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/python/Layer/requirements.txt +1 -0
- package/resources/cdk-apps/sam_cdk_integ_app/src/rest-api-definition.yaml +12 -0
- package/resources/cli-regression-patches/v1.119.0/NOTES.md +5 -0
- package/resources/cli-regression-patches/v1.119.0/cli.integtest.js +659 -0
- package/resources/cli-regression-patches/v1.130.0/NOTES.md +12 -0
- package/resources/cli-regression-patches/v1.130.0/app/app.js +378 -0
- package/resources/cli-regression-patches/v1.130.0/bootstrapping.integtest.js +220 -0
- package/resources/cli-regression-patches/v1.44.0/NOTES.md +18 -0
- package/resources/cli-regression-patches/v1.44.0/bootstrapping.integtest.js +126 -0
- package/resources/cli-regression-patches/v1.44.0/test.sh +26 -0
- package/resources/cli-regression-patches/v1.61.1/NOTES.md +2 -0
- package/resources/cli-regression-patches/v1.61.1/skip-tests.txt +16 -0
- package/resources/cli-regression-patches/v1.62.0/NOTES.md +2 -0
- package/resources/cli-regression-patches/v1.62.0/aws-helpers.js +245 -0
- package/resources/cli-regression-patches/v1.63.0/NOTES.md +1 -0
- package/resources/cli-regression-patches/v1.63.0/skip-tests.txt +7 -0
- package/resources/cli-regression-patches/v1.64.0/NOTES.md +3 -0
- package/resources/cli-regression-patches/v1.64.0/cdk-helpers.js +325 -0
- package/resources/cli-regression-patches/v1.64.0/cli.integtest.js +599 -0
- package/resources/cli-regression-patches/v1.64.1/NOTES.md +3 -0
- package/resources/cli-regression-patches/v1.64.1/cdk-helpers.js +324 -0
- package/resources/cli-regression-patches/v1.64.1/cli.integtest.js +599 -0
- package/resources/cli-regression-patches/v1.67.0/NOTES.md +2 -0
- package/resources/cli-regression-patches/v1.67.0/cdk-helpers.js +331 -0
- package/resources/cloud-assemblies/0.36.0/InitStack.template.json +1 -0
- package/resources/cloud-assemblies/0.36.0/manifest.json +19 -0
- package/resources/cloud-assemblies/1.10.0-lookup-default-vpc/InitStack.template.json +2 -0
- package/resources/cloud-assemblies/1.10.0-lookup-default-vpc/manifest.json.js +37 -0
- package/resources/cloud-assemblies/1.10.0-request-azs/InitStack.template.json +2 -0
- package/resources/cloud-assemblies/1.10.0-request-azs/manifest.json.js +34 -0
- package/resources/integ.jest.config.js +25 -0
- package/skip-tests.txt +8 -0
- package/tests/cli-integ-tests/README.md +47 -0
- package/tests/cli-integ-tests/bootstrapping.integtest.d.ts +1 -0
- package/tests/cli-integ-tests/bootstrapping.integtest.js +271 -0
- package/tests/cli-integ-tests/cli.integtest.d.ts +1 -0
- package/tests/cli-integ-tests/cli.integtest.js +1029 -0
- package/tests/init-csharp/init-csharp.integtest.d.ts +1 -0
- package/tests/init-csharp/init-csharp.integtest.js +14 -0
- package/tests/init-fsharp/init-fsharp.integtest.d.ts +1 -0
- package/tests/init-fsharp/init-fsharp.integtest.js +14 -0
- package/tests/init-java/init-java.integtest.d.ts +1 -0
- package/tests/init-java/init-java.integtest.js +14 -0
- package/tests/init-javascript/init-javascript.integtest.d.ts +1 -0
- package/tests/init-javascript/init-javascript.integtest.js +15 -0
- package/tests/init-python/init-python.integtest.d.ts +1 -0
- package/tests/init-python/init-python.integtest.js +19 -0
- package/tests/init-typescript-app/init-typescript-app.integtest.d.ts +1 -0
- package/tests/init-typescript-app/init-typescript-app.integtest.js +49 -0
- package/tests/init-typescript-lib/init-typescript-lib.integtest.d.ts +1 -0
- package/tests/init-typescript-lib/init-typescript-lib.integtest.js +13 -0
- package/tests/uberpackage/uberpackage.integtest.d.ts +1 -0
- package/tests/uberpackage/uberpackage.integtest.js +11 -0
package/lib/with-sam.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.shellWithAction = exports.randomInteger = exports.SamIntegrationTestFixture = exports.withSamIntegrationFixture = exports.withSamIntegrationCdkApp = void 0;
|
|
4
|
+
const child_process = require("child_process");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const os = require("os");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const axios_1 = require("axios");
|
|
9
|
+
const resources_1 = require("./resources");
|
|
10
|
+
const shell_1 = require("./shell");
|
|
11
|
+
const with_aws_1 = require("./with-aws");
|
|
12
|
+
const with_cdk_app_1 = require("./with-cdk-app");
|
|
13
|
+
/**
|
|
14
|
+
* Higher order function to execute a block with a SAM Integration CDK app fixture
|
|
15
|
+
*/
|
|
16
|
+
function withSamIntegrationCdkApp(block) {
|
|
17
|
+
return async (context) => {
|
|
18
|
+
const randy = context.randomString;
|
|
19
|
+
const stackNamePrefix = `cdktest-${randy}`;
|
|
20
|
+
const integTestDir = path.join(os.tmpdir(), `cdk-integ-${randy}`);
|
|
21
|
+
context.log(` Stack prefix: ${stackNamePrefix}\n`);
|
|
22
|
+
context.log(` Test directory: ${integTestDir}\n`);
|
|
23
|
+
context.log(` Region: ${context.aws.region}\n`);
|
|
24
|
+
await with_cdk_app_1.cloneDirectory(path.join(resources_1.RESOURCES_DIR, 'cdk-apps', 'sam_cdk_integ_app'), integTestDir, context.output);
|
|
25
|
+
const fixture = new SamIntegrationTestFixture(integTestDir, stackNamePrefix, context.output, context.aws, context.randomString);
|
|
26
|
+
let success = true;
|
|
27
|
+
try {
|
|
28
|
+
const installationVersion = fixture.packages.requestedFrameworkVersion();
|
|
29
|
+
if (fixture.packages.majorVersion() === '1') {
|
|
30
|
+
await with_cdk_app_1.installNpmPackages(fixture, {
|
|
31
|
+
'@aws-cdk/aws-iam': installationVersion,
|
|
32
|
+
'@aws-cdk/aws-apigateway': installationVersion,
|
|
33
|
+
'@aws-cdk/aws-lambda': installationVersion,
|
|
34
|
+
'@aws-cdk/aws-lambda-go': installationVersion,
|
|
35
|
+
'@aws-cdk/aws-lambda-nodejs': installationVersion,
|
|
36
|
+
'@aws-cdk/aws-lambda-python': installationVersion,
|
|
37
|
+
'@aws-cdk/aws-logs': installationVersion,
|
|
38
|
+
'@aws-cdk/core': installationVersion,
|
|
39
|
+
'constructs': '^3',
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const alphaInstallationVersion = fixture.packages.requestedAlphaVersion();
|
|
44
|
+
await with_cdk_app_1.installNpmPackages(fixture, {
|
|
45
|
+
'aws-cdk-lib': installationVersion,
|
|
46
|
+
'@aws-cdk/aws-lambda-go-alpha': alphaInstallationVersion,
|
|
47
|
+
'@aws-cdk/aws-lambda-python-alpha': alphaInstallationVersion,
|
|
48
|
+
'constructs': '^10',
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
await block(fixture);
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
// We survive certain cases involving gopkg.in
|
|
55
|
+
if (errorCausedByGoPkg(e.message)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
success = false;
|
|
59
|
+
throw e;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
if (process.env.INTEG_NO_CLEAN) {
|
|
63
|
+
context.log(`Left test directory in '${integTestDir}' ($INTEG_NO_CLEAN)\n`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
await fixture.dispose(success);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
exports.withSamIntegrationCdkApp = withSamIntegrationCdkApp;
|
|
72
|
+
/**
|
|
73
|
+
* Return whether or not the error is being caused by gopkg.in being down
|
|
74
|
+
*
|
|
75
|
+
* Our Go build depends on https://gopkg.in/, which has errors pretty often
|
|
76
|
+
* (every couple of days). It is run by a single volunteer.
|
|
77
|
+
*/
|
|
78
|
+
function errorCausedByGoPkg(error) {
|
|
79
|
+
// The error is different depending on what request fails. Messages recognized:
|
|
80
|
+
////////////////////////////////////////////////////////////////////
|
|
81
|
+
// go: github.com/aws/aws-lambda-go@v1.28.0 requires
|
|
82
|
+
// gopkg.in/yaml.v3@v3.0.0-20200615113413-eeeca48fe776: invalid version: git ls-remote -q origin in /go/pkg/mod/cache/vcs/0901dc1ef67fcce1c9b3ae51078740de4a0e2dc673e720584ac302973af82f36: exit status 128:
|
|
83
|
+
// remote: Cannot obtain refs from GitHub: cannot talk to GitHub: Get https://github.com/go-yaml/yaml.git/info/refs?service=git-upload-pack: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
|
|
84
|
+
// fatal: unable to access 'https://gopkg.in/yaml.v3/': The requested URL returned error: 502
|
|
85
|
+
////////////////////////////////////////////////////////////////////
|
|
86
|
+
// go: downloading github.com/aws/aws-lambda-go v1.28.0
|
|
87
|
+
// go: github.com/aws/aws-lambda-go@v1.28.0 requires
|
|
88
|
+
// gopkg.in/yaml.v3@v3.0.0-20200615113413-eeeca48fe776: unrecognized import path "gopkg.in/yaml.v3": reading https://gopkg.in/yaml.v3?go-get=1: 502 Bad Gateway
|
|
89
|
+
// server response: Cannot obtain refs from GitHub: cannot talk to GitHub: Get https://github.com/go-yaml/yaml.git/info/refs?service=git-upload-pack: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
|
|
90
|
+
////////////////////////////////////////////////////////////////////
|
|
91
|
+
// go: github.com/aws/aws-lambda-go@v1.28.0 requires
|
|
92
|
+
// gopkg.in/yaml.v3@v3.0.0-20200615113413-eeeca48fe776: invalid version: git fetch -f origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /go/pkg/mod/cache/vcs/0901dc1ef67fcce1c9b3ae51078740de4a0e2dc673e720584ac302973af82f36: exit status 128:
|
|
93
|
+
// error: RPC failed; HTTP 502 curl 22 The requested URL returned error: 502
|
|
94
|
+
// fatal: the remote end hung up unexpectedly
|
|
95
|
+
////////////////////////////////////////////////////////////////////
|
|
96
|
+
return (error.includes('gopkg\.in.*invalid version.*exit status 128')
|
|
97
|
+
|| error.match(/unrecognized import path[^\n]gopkg\.in/));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* SAM Integration test fixture for CDK - SAM integration test cases
|
|
101
|
+
*/
|
|
102
|
+
function withSamIntegrationFixture(block) {
|
|
103
|
+
return with_aws_1.withAws(withSamIntegrationCdkApp(block));
|
|
104
|
+
}
|
|
105
|
+
exports.withSamIntegrationFixture = withSamIntegrationFixture;
|
|
106
|
+
class SamIntegrationTestFixture extends with_cdk_app_1.TestFixture {
|
|
107
|
+
async samShell(command, filter, action, options = {}) {
|
|
108
|
+
return shellWithAction(command, filter, action, {
|
|
109
|
+
output: this.output,
|
|
110
|
+
cwd: path.join(this.integTestDir, 'cdk.out').toString(),
|
|
111
|
+
...options,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async samBuild(stackName) {
|
|
115
|
+
const fullStackName = this.fullStackName(stackName);
|
|
116
|
+
const templatePath = path.join(this.integTestDir, 'cdk.out', `${fullStackName}.template.json`);
|
|
117
|
+
const args = ['--template', templatePath.toString()];
|
|
118
|
+
return this.samShell(['sam', 'build', ...args]);
|
|
119
|
+
}
|
|
120
|
+
async samLocalStartApi(stackName, isBuilt, port, apiPath) {
|
|
121
|
+
const fullStackName = this.fullStackName(stackName);
|
|
122
|
+
const templatePath = path.join(this.integTestDir, 'cdk.out', `${fullStackName}.template.json`);
|
|
123
|
+
const args = isBuilt ? [] : ['--template', templatePath.toString()];
|
|
124
|
+
args.push('--port');
|
|
125
|
+
args.push(port.toString());
|
|
126
|
+
return this.samShell(['sam', 'local', 'start-api', ...args], '(Press CTRL+C to quit)', () => {
|
|
127
|
+
return new Promise((resolve, reject) => {
|
|
128
|
+
axios_1.default.get(`http://127.0.0.1:${port}${apiPath}`).then(resp => {
|
|
129
|
+
resolve(resp.data);
|
|
130
|
+
}).catch(error => {
|
|
131
|
+
reject(new Error(`Failed to invoke api path ${apiPath} on port ${port} with error ${error}`));
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Cleanup leftover stacks and buckets
|
|
138
|
+
*/
|
|
139
|
+
async dispose(success) {
|
|
140
|
+
// If the tests completed successfully, happily delete the fixture
|
|
141
|
+
// (otherwise leave it for humans to inspect)
|
|
142
|
+
if (success) {
|
|
143
|
+
shell_1.rimraf(this.integTestDir);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.SamIntegrationTestFixture = SamIntegrationTestFixture;
|
|
148
|
+
function randomInteger(min, max) {
|
|
149
|
+
return Math.floor(Math.random() * (max - min) + min);
|
|
150
|
+
}
|
|
151
|
+
exports.randomInteger = randomInteger;
|
|
152
|
+
/**
|
|
153
|
+
* A shell command that does what you want
|
|
154
|
+
*
|
|
155
|
+
* Is platform-aware, handles errors nicely.
|
|
156
|
+
*/
|
|
157
|
+
async function shellWithAction(command, filter, action, options = {}) {
|
|
158
|
+
if (options.modEnv && options.env) {
|
|
159
|
+
throw new Error('Use either env or modEnv but not both');
|
|
160
|
+
}
|
|
161
|
+
options.output?.write(`💻 ${command.join(' ')}\n`);
|
|
162
|
+
const env = options.env ?? (options.modEnv ? { ...process.env, ...options.modEnv } : undefined);
|
|
163
|
+
const child = child_process.spawn(command[0], command.slice(1), {
|
|
164
|
+
...options,
|
|
165
|
+
env,
|
|
166
|
+
// Need this for Windows where we want .cmd and .bat to be found as well.
|
|
167
|
+
shell: true,
|
|
168
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
169
|
+
});
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
const out = new Array();
|
|
172
|
+
const stdout = new Array();
|
|
173
|
+
const stderr = new Array();
|
|
174
|
+
let actionSucceeded = false;
|
|
175
|
+
let actionOutput;
|
|
176
|
+
let actionExecuted = false;
|
|
177
|
+
function executeAction(chunk) {
|
|
178
|
+
out.push(chunk);
|
|
179
|
+
if (!actionExecuted && typeof filter === 'string' && out.toString().includes(filter) && typeof action === 'function') {
|
|
180
|
+
actionExecuted = true;
|
|
181
|
+
options.output?.write('before executing action');
|
|
182
|
+
action().then((output) => {
|
|
183
|
+
options.output?.write(`action output is ${output}`);
|
|
184
|
+
actionOutput = output;
|
|
185
|
+
actionSucceeded = true;
|
|
186
|
+
}).catch((error) => {
|
|
187
|
+
options.output?.write(`action error is ${error}`);
|
|
188
|
+
actionSucceeded = false;
|
|
189
|
+
actionOutput = error;
|
|
190
|
+
}).finally(() => {
|
|
191
|
+
options.output?.write('terminate sam sub process');
|
|
192
|
+
killSubProcess(child, command.join(' '));
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
child.stdout.on('data', chunk => {
|
|
197
|
+
options.output?.write(chunk);
|
|
198
|
+
stdout.push(chunk);
|
|
199
|
+
executeAction(chunk);
|
|
200
|
+
});
|
|
201
|
+
child.stderr.on('data', chunk => {
|
|
202
|
+
options.output?.write(chunk);
|
|
203
|
+
if (options.captureStderr ?? true) {
|
|
204
|
+
stderr.push(chunk);
|
|
205
|
+
}
|
|
206
|
+
executeAction(chunk);
|
|
207
|
+
});
|
|
208
|
+
child.once('error', reject);
|
|
209
|
+
child.once('close', code => {
|
|
210
|
+
const output = (Buffer.concat(stdout).toString('utf-8') + Buffer.concat(stderr).toString('utf-8')).trim();
|
|
211
|
+
if (code == null || code === 0 || options.allowErrExit) {
|
|
212
|
+
let result = new Array();
|
|
213
|
+
result.push(actionOutput);
|
|
214
|
+
result.push(output);
|
|
215
|
+
resolve({
|
|
216
|
+
actionSucceeded: actionSucceeded,
|
|
217
|
+
actionOutput: actionOutput,
|
|
218
|
+
shellOutput: output,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
reject(new Error(`'${command.join(' ')}' exited with error code ${code}. Output: \n${output}`));
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
exports.shellWithAction = shellWithAction;
|
|
228
|
+
function killSubProcess(child, command) {
|
|
229
|
+
/**
|
|
230
|
+
* Check if the sub process is running in container, so child_process.spawn will
|
|
231
|
+
* create multiple processes, and to kill all of them we need to run different logic
|
|
232
|
+
*/
|
|
233
|
+
if (fs.existsSync('/.dockerenv')) {
|
|
234
|
+
child_process.exec(`for pid in $(ps -ef | grep "${command}" | awk '{print $2}'); do kill -2 $pid; done`);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
child.kill('SIGINT');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aC1zYW0uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ3aXRoLXNhbS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQ0FBK0M7QUFDL0MseUJBQXlCO0FBQ3pCLHlCQUF5QjtBQUN6Qiw2QkFBNkI7QUFDN0IsaUNBQTBCO0FBRTFCLDJDQUE0QztBQUM1QyxtQ0FBK0M7QUFDL0MseUNBQWlEO0FBQ2pELGlEQUFpRjtBQVNqRjs7R0FFRztBQUNILFNBQWdCLHdCQUF3QixDQUFxQyxLQUE0RDtJQUN2SSxPQUFPLEtBQUssRUFBRSxPQUFVLEVBQUUsRUFBRTtRQUMxQixNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDO1FBQ25DLE1BQU0sZUFBZSxHQUFHLFdBQVcsS0FBSyxFQUFFLENBQUM7UUFDM0MsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsYUFBYSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBRWxFLE9BQU8sQ0FBQyxHQUFHLENBQUMsb0JBQW9CLGVBQWUsSUFBSSxDQUFDLENBQUM7UUFDckQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsWUFBWSxJQUFJLENBQUMsQ0FBQztRQUNsRCxPQUFPLENBQUMsR0FBRyxDQUFDLG9CQUFvQixPQUFPLENBQUMsR0FBRyxDQUFDLE1BQU0sSUFBSSxDQUFDLENBQUM7UUFFeEQsTUFBTSw2QkFBYyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMseUJBQWEsRUFBRSxVQUFVLEVBQUUsbUJBQW1CLENBQUMsRUFBRSxZQUFZLEVBQUUsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzlHLE1BQU0sT0FBTyxHQUFHLElBQUkseUJBQXlCLENBQzNDLFlBQVksRUFDWixlQUFlLEVBQ2YsT0FBTyxDQUFDLE1BQU0sRUFDZCxPQUFPLENBQUMsR0FBRyxFQUNYLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUV4QixJQUFJLE9B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QUMsQ0FBQztJQUVMLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQS9FRCwwQ0ErRUM7QUFFRCxTQUFTLGNBQWMsQ0FBQyxLQUFpQyxFQUFFLE9BQWU7SUFDeEU7OztPQUdHO0lBQ0gsSUFBSSxFQUFFLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQyxFQUFFO1FBQ2hDLGFBQWEsQ0FBQyxJQUFJLENBQUMsK0JBQStCLE9BQU8sOENBQThDLENBQUMsQ0FBQztLQUMxRztTQUFNO1FBQ0wsS0FBSyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztLQUN0QjtBQUVILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBjaGlsZF9wcm9jZXNzIGZyb20gJ2NoaWxkX3Byb2Nlc3MnO1xuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCBheGlvcyBmcm9tICdheGlvcyc7XG5pbXBvcnQgeyBUZXN0Q29udGV4dCB9IGZyb20gJy4vaW50ZWctdGVzdCc7XG5pbXBvcnQgeyBSRVNPVVJDRVNfRElSIH0gZnJvbSAnLi9yZXNvdXJjZXMnO1xuaW1wb3J0IHsgU2hlbGxPcHRpb25zLCByaW1yYWYgfSBmcm9tICcuL3NoZWxsJztcbmltcG9ydCB7IEF3c0NvbnRleHQsIHdpdGhBd3MgfSBmcm9tICcuL3dpdGgtYXdzJztcbmltcG9ydCB7IGNsb25lRGlyZWN0b3J5LCBpbnN0YWxsTnBtUGFja2FnZXMsIFRlc3RGaXh0dXJlIH0gZnJvbSAnLi93aXRoLWNkay1hcHAnO1xuXG5cbmV4cG9ydCBpbnRlcmZhY2UgQWN0aW9uT3V0cHV0IHtcbiAgYWN0aW9uU3VjY2VlZGVkPzogYm9vbGVhbjtcbiAgYWN0aW9uT3V0cHV0PzogYW55O1xuICBzaGVsbE91dHB1dD86IHN0cmluZztcbn1cblxuLyoqXG4gKiBIaWdoZXIgb3JkZXIgZnVuY3Rpb24gdG8gZXhlY3V0ZSBhIGJsb2NrIHdpdGggYSBTQU0gSW50ZWdyYXRpb24gQ0RLIGFwcCBmaXh0dXJlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3aXRoU2FtSW50ZWdyYXRpb25DZGtBcHA8QSBleHRlbmRzIFRlc3RDb250ZXh0ICYgQXdzQ29udGV4dD4oYmxvY2s6IChjb250ZXh0OiBTYW1JbnRlZ3JhdGlvblRlc3RGaXh0dXJlKSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gIHJldHVybiBhc3luYyAoY29udGV4dDogQSkgPT4ge1xuICAgIGNvbnN0IHJhbmR5ID0gY29udGV4dC5yYW5kb21TdHJpbmc7XG4gICAgY29uc3Qgc3RhY2tOYW1lUHJlZml4ID0gYGNka3Rlc3QtJHtyYW5keX1gO1xuICAgIGNvbnN0IGludGVnVGVzdERpciA9IHBhdGguam9pbihvcy50bXBkaXIoKSwgYGNkay1pbnRlZy0ke3JhbmR5fWApO1xuXG4gICAgY29udGV4dC5sb2coYCBTdGFjayBwcmVmaXg6ICAgJHtzdGFja05hbWVQcmVmaXh9XFxuYCk7XG4gICAgY29udGV4dC5sb2coYCBUZXN0IGRpcmVjdG9yeTogJHtpbnRlZ1Rlc3REaXJ9XFxuYCk7XG4gICAgY29udGV4dC5sb2coYCBSZWdpb246ICAgICAgICAgJHtjb250ZXh0LmF3cy5yZWdpb259XFxuYCk7XG5cbiAgICBhd2FpdCBjbG9uZURpcmVjdG9yeShwYXRoLmpvaW4oUkVTT1VSQ0VTX0RJUiwgJ2Nkay1hcHBzJywgJ3NhbV9jZGtfaW50ZWdfYXBwJyksIGludGVnVGVzdERpciwgY29udGV4dC5vdXRwdXQpO1xuICAgIGNvbnN0IGZpeHR1cmUgPSBuZXcgU2FtSW50ZWdyYXRpb25UZXN0Rml4dHVyZShcbiAgICAgIGludGVnVGVzdERpcixcbiAgICAgIHN0YWNrTmFtZVByZWZpeCxcbiAgICAgIGNvbnRleHQub3V0cHV0LFxuICAgICAgY29udGV4dC5hd3MsXG4gICAgICBjb250ZXh0LnJhbmRvbVN0cmluZyk7XG5cbiAgICBsZXQgc3VjY2VzcyA9IHRydWU7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGluc3RhbGxhdGlvblZlcnNpb24gPSBmaXh0dXJlLnBhY2thZ2VzLnJlcXVlc3RlZEZyYW1ld29ya1ZlcnNpb24oKTtcblxuICAgICAgaWYgKGZpeHR1cmUucGFja2FnZXMubWFqb3JWZXJzaW9uKCkgPT09ICcxJykge1xuICAgICAgICBhd2FpdCBpbnN0YWxsTnBtUGFja2FnZXMoZml4dHVyZSwge1xuICAgICAgICAgICdAYXdzLWNkay9hd3MtaWFtJzogaW5zdGFsbGF0aW9uVmVyc2lvbixcbiAgICAgICAgICAnQGF3cy1jZGsvYXdzLWFwaWdhdGV3YXknOiBpbnN0YWxsYXRpb25WZXJzaW9uLFxuICAgICAgICAgICdAYXdzLWNkay9hd3MtbGFtYmRhJzogaW5zdGFsbGF0aW9uVmVyc2lvbixcbiAgICAgICAgICAnQGF3cy1jZGsvYXdzLWxhbWJkYS1nbyc6IGluc3RhbGxhdGlvblZlcnNpb24sXG4gICAgICAgICAgJ0Bhd3MtY2RrL2F3cy1sYW1iZGEtbm9kZWpzJzogaW5zdGFsbGF0aW9uVmVyc2lvbixcbiAgICAgICAgICAnQGF3cy1jZGsvYXdzLWxhbWJkYS1weXRob24nOiBpbnN0YWxsYXRpb25WZXJzaW9uLFxuICAgICAgICAgICdAYXdzLWNkay9hd3MtbG9ncyc6IGluc3RhbGxhdGlvblZlcnNpb24sXG4gICAgICAgICAgJ0Bhd3MtY2RrL2NvcmUnOiBpbnN0YWxsYXRpb25WZXJzaW9uLFxuICAgICAgICAgICdjb25zdHJ1Y3RzJzogJ14zJyxcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBhbHBoYUluc3RhbGxhdGlvblZlcnNpb24gPSBmaXh0dXJlLnBhY2thZ2VzLnJlcXVlc3RlZEFscGhhVmVyc2lvbigpO1xuICAgICAgICBhd2FpdCBpbnN0YWxsTnBtUGFja2FnZXMoZml4dHVyZSwge1xuICAgICAgICAgICdhd3MtY2RrLWxpYic6IGluc3RhbGxhdGlvblZlcnNpb24sXG4gICAgICAgICAgJ0Bhd3MtY2RrL2F3cy1sYW1iZGEtZ28tYWxwaGEnOiBhbHBoYUluc3RhbGxhdGlvblZlcnNpb24sXG4gICAgICAgICAgJ0Bhd3MtY2RrL2F3cy1sYW1iZGEtcHl0aG9uLWFscGhhJzogYWxwaGFJbnN0YWxsYXRpb25WZXJzaW9uLFxuICAgICAgICAgICdjb25zdHJ1Y3RzJzogJ14xMCcsXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgYXdhaXQgYmxvY2soZml4dHVyZSk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgLy8gV2Ugc3Vydml2ZSBjZXJ0YWluIGNhc2VzIGludm9sdmluZyBnb3BrZy5pblxuICAgICAgaWYgKGVycm9yQ2F1c2VkQnlHb1BrZyhlLm1lc3NhZ2UpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHN1Y2Nlc3MgPSBmYWxzZTtcbiAgICAgIHRocm93IGU7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGlmIChwcm9jZXNzLmVudi5JTlRFR19OT19DTEVBTikge1xuICAgICAgICBjb250ZXh0LmxvZyhgTGVmdCB0ZXN0IGRpcmVjdG9yeSBpbiAnJHtpbnRlZ1Rlc3REaXJ9JyAoJElOVEVHX05PX0NMRUFOKVxcbmApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYXdhaXQgZml4dHVyZS5kaXNwb3NlKHN1Y2Nlc3MpO1xuICAgICAgfVxuICAgIH1cbiAgfTtcbn1cblxuLyoqXG4gKiBSZXR1cm4gd2hldGhlciBvciBub3QgdGhlIGVycm9yIGlzIGJlaW5nIGNhdXNlZCBieSBnb3BrZy5pbiBiZWluZyBkb3duXG4gKlxuICogT3VyIEdvIGJ1aWxkIGRlcGVuZHMgb24gaHR0cHM6Ly9nb3BrZy5pbi8sIHdoaWNoIGhhcyBlcnJvcnMgcHJldHR5IG9mdGVuXG4gKiAoZXZlcnkgY291cGxlIG9mIGRheXMpLiBJdCBpcyBydW4gYnkgYSBzaW5nbGUgdm9sdW50ZWVyLlxuICovXG5mdW5jdGlvbiBlcnJvckNhdXNlZEJ5R29Qa2coZXJyb3I6IHN0cmluZykge1xuICAvLyBUaGUgZXJyb3IgaXMgZGlmZmVyZW50IGRlcGVuZGluZyBvbiB3aGF0IHJlcXVlc3QgZmFpbHMuIE1lc3NhZ2VzIHJlY29nbml6ZWQ6XG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4gIC8vICAgIGdvOiBnaXRodWIuY29tL2F3cy9hd3MtbGFtYmRhLWdvQHYxLjI4LjAgcmVxdWlyZXNcbiAgLy8gICAgICAgIGdvcGtnLmluL3lhbWwudjNAdjMuMC4wLTIwMjAwNjE1MTEzNDEzLWVlZWNhNDhmZTc3NjogaW52YWxpZCB2ZXJzaW9uOiBnaXQgbHMtcmVtb3RlIC1xIG9yaWdpbiBpbiAvZ28vcGtnL21vZC9jYWNoZS92Y3MvMDkwMWRjMWVmNjdmY2NlMWM5YjNhZTUxMDc4NzQwZGU0YTBlMmRjNjczZTcyMDU4NGFjMzAyOTczYWY4MmYzNjogZXhpdCBzdGF0dXMgMTI4OlxuICAvLyAgICAgICAgcmVtb3RlOiBDYW5ub3Qgb2J0YWluIHJlZnMgZnJvbSBHaXRIdWI6IGNhbm5vdCB0YWxrIHRvIEdpdEh1YjogR2V0IGh0dHBzOi8vZ2l0aHViLmNvbS9nby15YW1sL3lhbWwuZ2l0L2luZm8vcmVmcz9zZXJ2aWNlPWdpdC11cGxvYWQtcGFjazogbmV0L2h0dHA6IHJlcXVlc3QgY2FuY2VsZWQgKENsaWVudC5UaW1lb3V0IGV4Y2VlZGVkIHdoaWxlIGF3YWl0aW5nIGhlYWRlcnMpXG4gIC8vICAgICAgICBmYXRhbDogdW5hYmxlIHRvIGFjY2VzcyAnaHR0cHM6Ly9nb3BrZy5pbi95YW1sLnYzLyc6IFRoZSByZXF1ZXN0ZWQgVVJMIHJldHVybmVkIGVycm9yOiA1MDJcbiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cbiAgLy8gICAgZ286IGRvd25sb2FkaW5nIGdpdGh1Yi5jb20vYXdzL2F3cy1sYW1iZGEtZ28gdjEuMjguMFxuICAvLyAgICBnbzogZ2l0aHViLmNvbS9hd3MvYXdzLWxhbWJkYS1nb0B2MS4yOC4wIHJlcXVpcmVzXG4gIC8vICAgICAgICBnb3BrZy5pbi95YW1sLnYzQHYzLjAuMC0yMDIwMDYxNTExMzQxMy1lZWVjYTQ4ZmU3NzY6IHVucmVjb2duaXplZCBpbXBvcnQgcGF0aCBcImdvcGtnLmluL3lhbWwudjNcIjogcmVhZGluZyBodHRwczovL2dvcGtnLmluL3lhbWwudjM/Z28tZ2V0PTE6IDUwMiBCYWQgR2F0ZXdheVxuICAvLyAgICAgICAgc2VydmVyIHJlc3BvbnNlOiBDYW5ub3Qgb2J0YWluIHJlZnMgZnJvbSBHaXRIdWI6IGNhbm5vdCB0YWxrIHRvIEdpdEh1YjogR2V0IGh0dHBzOi8vZ2l0aHViLmNvbS9nby15YW1sL3lhbWwuZ2l0L2luZm8vcmVmcz9zZXJ2aWNlPWdpdC11cGxvYWQtcGFjazogbmV0L2h0dHA6IHJlcXVlc3QgY2FuY2VsZWQgKENsaWVudC5UaW1lb3V0IGV4Y2VlZGVkIHdoaWxlIGF3YWl0aW5nIGhlYWRlcnMpXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4gIC8vICAgIGdvOiBnaXRodWIuY29tL2F3cy9hd3MtbGFtYmRhLWdvQHYxLjI4LjAgcmVxdWlyZXNcbiAgLy8gICAgICAgIGdvcGtnLmluL3lhbWwudjNAdjMuMC4wLTIwMjAwNjE1MTEzNDEzLWVlZWNhNDhmZTc3NjogaW52YWxpZCB2ZXJzaW9uOiBnaXQgZmV0Y2ggLWYgb3JpZ2luIHJlZnMvaGVhZHMvKjpyZWZzL2hlYWRzLyogcmVmcy90YWdzLyo6cmVmcy90YWdzLyogaW4gL2dvL3BrZy9tb2QvY2FjaGUvdmNzLzA5MDFkYzFlZjY3ZmNjZTFjOWIzYWU1MTA3ODc0MGRlNGEwZTJkYzY3M2U3MjA1ODRhYzMwMjk3M2FmODJmMzY6IGV4aXQgc3RhdHVzIDEyODpcbiAgLy8gICAgICAgIGVycm9yOiBSUEMgZmFpbGVkOyBIVFRQIDUwMiBjdXJsIDIyIFRoZSByZXF1ZXN0ZWQgVVJMIHJldHVybmVkIGVycm9yOiA1MDJcbiAgLy8gICAgICAgIGZhdGFsOiB0aGUgcmVtb3RlIGVuZCBodW5nIHVwIHVuZXhwZWN0ZWRseVxuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuXG4gIHJldHVybiAoZXJyb3IuaW5jbHVkZXMoJ2dvcGtnXFwuaW4uKmludmFsaWQgdmVyc2lvbi4qZXhpdCBzdGF0dXMgMTI4JylcbiAgICB8fCBlcnJvci5tYXRjaCgvdW5yZWNvZ25pemVkIGltcG9ydCBwYXRoW15cXG5dZ29wa2dcXC5pbi8pKTtcbn1cblxuLyoqXG4gKiBTQU0gSW50ZWdyYXRpb24gdGVzdCBmaXh0dXJlIGZvciBDREsgLSBTQU0gaW50ZWdyYXRpb24gdGVzdCBjYXNlc1xuICovXG5leHBvcnQgZnVuY3Rpb24gd2l0aFNhbUludGVncmF0aW9uRml4dHVyZShibG9jazogKGNvbnRleHQ6IFNhbUludGVncmF0aW9uVGVzdEZpeHR1cmUpID0+IFByb21pc2U8dm9pZD4pIHtcbiAgcmV0dXJuIHdpdGhBd3M8VGVzdENvbnRleHQ+KHdpdGhTYW1JbnRlZ3JhdGlvbkNka0FwcChibG9jaykpO1xufVxuXG5leHBvcnQgY2xhc3MgU2FtSW50ZWdyYXRpb25UZXN0Rml4dHVyZSBleHRlbmRzIFRlc3RGaXh0dXJlIHtcbiAgcHVibGljIGFzeW5jIHNhbVNoZWxsKGNvbW1hbmQ6IHN0cmluZ1tdLCBmaWx0ZXI/OiBzdHJpbmcsIGFjdGlvbj86ICgpID0+IGFueSwgb3B0aW9uczogT21pdDxTaGVsbE9wdGlvbnMsICdjd2QnIHwgJ291dHB1dCc+ID0ge30pOiBQcm9taXNlPEFjdGlvbk91dHB1dD4ge1xuICAgIHJldHVybiBzaGVsbFdpdGhBY3Rpb24oY29tbWFuZCwgZmlsdGVyLCBhY3Rpb24sIHtcbiAgICAgIG91dHB1dDogdGhpcy5vdXRwdXQsXG4gICAgICBjd2Q6IHBhdGguam9pbih0aGlzLmludGVnVGVzdERpciwgJ2Nkay5vdXQnKS50b1N0cmluZygpLFxuICAgICAgLi4ub3B0aW9ucyxcbiAgICB9KTtcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBzYW1CdWlsZChzdGFja05hbWU6IHN0cmluZykge1xuICAgIGNvbnN0IGZ1bGxTdGFja05hbWUgPSB0aGlzLmZ1bGxTdGFja05hbWUoc3RhY2tOYW1lKTtcbiAgICBjb25zdCB0ZW1wbGF0ZVBhdGggPSBwYXRoLmpvaW4odGhpcy5pbnRlZ1Rlc3REaXIsICdjZGsub3V0JywgYCR7ZnVsbFN0YWNrTmFtZX0udGVtcGxhdGUuanNvbmApO1xuICAgIGNvbnN0IGFyZ3MgPSBbJy0tdGVtcGxhdGUnLCB0ZW1wbGF0ZVBhdGgudG9TdHJpbmcoKV07XG4gICAgcmV0dXJuIHRoaXMuc2FtU2hlbGwoWydzYW0nLCAnYnVpbGQnLCAuLi5hcmdzXSk7XG4gIH1cblxuICBwdWJsaWMgYXN5bmMgc2FtTG9jYWxTdGFydEFwaShzdGFja05hbWU6IHN0cmluZywgaXNCdWlsdDogYm9vbGVhbiwgcG9ydDogbnVtYmVyLCBhcGlQYXRoOiBzdHJpbmcpOiBQcm9taXNlPEFjdGlvbk91dHB1dD4ge1xuICAgIGNvbnN0IGZ1bGxTdGFja05hbWUgPSB0aGlzLmZ1bGxTdGFja05hbWUoc3RhY2tOYW1lKTtcbiAgICBjb25zdCB0ZW1wbGF0ZVBhdGggPSBwYXRoLmpvaW4odGhpcy5pbnRlZ1Rlc3REaXIsICdjZGsub3V0JywgYCR7ZnVsbFN0YWNrTmFtZX0udGVtcGxhdGUuanNvbmApO1xuICAgIGNvbnN0IGFyZ3MgPSBpc0J1aWx0PyBbXSA6IFsnLS10ZW1wbGF0ZScsIHRlbXBsYXRlUGF0aC50b1N0cmluZygpXTtcbiAgICBhcmdzLnB1c2goJy0tcG9ydCcpO1xuICAgIGFyZ3MucHVzaChwb3J0LnRvU3RyaW5nKCkpO1xuXG4gICAgcmV0dXJuIHRoaXMuc2FtU2hlbGwoWydzYW0nLCAnbG9jYWwnLCAnc3RhcnQtYXBpJywgLi4uYXJnc10sICcoUHJlc3MgQ1RSTCtDIHRvIHF1aXQpJywgKCk9PntcbiAgICAgIHJldHVybiBuZXcgUHJvbWlzZTxBY3Rpb25PdXRwdXQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgYXhpb3MuZ2V0KGBodHRwOi8vMTI3LjAuMC4xOiR7cG9ydH0ke2FwaVBhdGh9YCkudGhlbiggcmVzcCA9PiB7XG4gICAgICAgICAgcmVzb2x2ZShyZXNwLmRhdGEpO1xuICAgICAgICB9KS5jYXRjaCggZXJyb3IgPT4ge1xuICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBpbnZva2UgYXBpIHBhdGggJHthcGlQYXRofSBvbiBwb3J0ICR7cG9ydH0gd2l0aCBlcnJvciAke2Vycm9yfWApKTtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhbnVwIGxlZnRvdmVyIHN0YWNrcyBhbmQgYnVja2V0c1xuICAgKi9cbiAgcHVibGljIGFzeW5jIGRpc3Bvc2Uoc3VjY2VzczogYm9vbGVhbikge1xuICAgIC8vIElmIHRoZSB0ZXN0cyBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5LCBoYXBwaWx5IGRlbGV0ZSB0aGUgZml4dHVyZVxuICAgIC8vIChvdGhlcndpc2UgbGVhdmUgaXQgZm9yIGh1bWFucyB0byBpbnNwZWN0KVxuICAgIGlmIChzdWNjZXNzKSB7XG4gICAgICByaW1yYWYodGhpcy5pbnRlZ1Rlc3REaXIpO1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcmFuZG9tSW50ZWdlcihtaW46IG51bWJlciwgbWF4OiBudW1iZXIpIHtcbiAgcmV0dXJuIE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIChtYXggLSBtaW4pICsgbWluKTtcbn1cblxuLyoqXG4gKiBBIHNoZWxsIGNvbW1hbmQgdGhhdCBkb2VzIHdoYXQgeW91IHdhbnRcbiAqXG4gKiBJcyBwbGF0Zm9ybS1hd2FyZSwgaGFuZGxlcyBlcnJvcnMgbmljZWx5LlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2hlbGxXaXRoQWN0aW9uKFxuICBjb21tYW5kOiBzdHJpbmdbXSwgZmlsdGVyPzogc3RyaW5nLCBhY3Rpb24/OiAoKSA9PiBQcm9taXNlPGFueT4sIG9wdGlvbnM6IFNoZWxsT3B0aW9ucyA9IHt9KTogUHJvbWlzZTxBY3Rpb25PdXRwdXQ+IHtcbiAgaWYgKG9wdGlvbnMubW9kRW52ICYmIG9wdGlvbnMuZW52KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdVc2UgZWl0aGVyIGVudiBvciBtb2RFbnYgYnV0IG5vdCBib3RoJyk7XG4gIH1cblxuICBvcHRpb25zLm91dHB1dD8ud3JpdGUoYPCfkrsgJHtjb21tYW5kLmpvaW4oJyAnKX1cXG5gKTtcblxuICBjb25zdCBlbnYgPSBvcHRpb25zLmVudiA/PyAob3B0aW9ucy5tb2RFbnYgPyB7IC4uLnByb2Nlc3MuZW52LCAuLi5vcHRpb25zLm1vZEVudiB9IDogdW5kZWZpbmVkKTtcblxuICBjb25zdCBjaGlsZCA9IGNoaWxkX3Byb2Nlc3Muc3Bhd24oY29tbWFuZFswXSwgY29tbWFuZC5zbGljZSgxKSwge1xuICAgIC4uLm9wdGlvbnMsXG4gICAgZW52LFxuICAgIC8vIE5lZWQgdGhpcyBmb3IgV2luZG93cyB3aGVyZSB3ZSB3YW50IC5jbWQgYW5kIC5iYXQgdG8gYmUgZm91bmQgYXMgd2VsbC5cbiAgICBzaGVsbDogdHJ1ZSxcbiAgICBzdGRpbzogWydpZ25vcmUnLCAncGlwZScsICdwaXBlJ10sXG4gIH0pO1xuXG4gIHJldHVybiBuZXcgUHJvbWlzZTxBY3Rpb25PdXRwdXQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBjb25zdCBvdXQgPSBuZXcgQXJyYXk8QnVmZmVyPigpO1xuICAgIGNvbnN0IHN0ZG91dCA9IG5ldyBBcnJheTxCdWZmZXI+KCk7XG4gICAgY29uc3Qgc3RkZXJyID0gbmV3IEFycmF5PEJ1ZmZlcj4oKTtcbiAgICBsZXQgYWN0aW9uU3VjY2VlZGVkID0gZmFsc2U7XG4gICAgbGV0IGFjdGlvbk91dHB1dDogYW55O1xuICAgIGxldCBhY3Rpb25FeGVjdXRlZCA9IGZhbHNlO1xuXG4gICAgZnVuY3Rpb24gZXhlY3V0ZUFjdGlvbihjaHVuazogYW55KSB7XG4gICAgICBvdXQucHVzaChjaHVuayk7XG4gICAgICBpZiAoIWFjdGlvbkV4ZWN1dGVkICYmIHR5cGVvZiBmaWx0ZXIgPT09ICdzdHJpbmcnICYmIG91dC50b1N0cmluZygpLmluY2x1ZGVzKGZpbHRlcikgJiYgdHlwZW9mIGFjdGlvbiA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBhY3Rpb25FeGVjdXRlZCA9IHRydWU7XG4gICAgICAgIG9wdGlvbnMub3V0cHV0Py53cml0ZSgnYmVmb3JlIGV4ZWN1dGluZyBhY3Rpb24nKTtcbiAgICAgICAgYWN0aW9uKCkudGhlbigob3V0cHV0KSA9PiB7XG4gICAgICAgICAgb3B0aW9ucy5vdXRwdXQ/LndyaXRlKGBhY3Rpb24gb3V0cHV0IGlzICR7b3V0cHV0fWApO1xuICAgICAgICAgIGFjdGlvbk91dHB1dCA9IG91dHB1dDtcbiAgICAgICAgICBhY3Rpb25TdWNjZWVkZWQgPSB0cnVlO1xuICAgICAgICB9KS5jYXRjaCgoZXJyb3IpID0+IHtcbiAgICAgICAgICBvcHRpb25zLm91dHB1dD8ud3JpdGUoYGFjdGlvbiBlcnJvciBpcyAke2Vycm9yfWApO1xuICAgICAgICAgIGFjdGlvblN1Y2NlZWRlZCA9IGZhbHNlO1xuICAgICAgICAgIGFjdGlvbk91dHB1dCA9IGVycm9yO1xuICAgICAgICB9KS5maW5hbGx5KCgpID0+IHtcbiAgICAgICAgICBvcHRpb25zLm91dHB1dD8ud3JpdGUoJ3Rlcm1pbmF0ZSBzYW0gc3ViIHByb2Nlc3MnKTtcbiAgICAgICAgICBraWxsU3ViUHJvY2VzcyhjaGlsZCwgY29tbWFuZC5qb2luKCcgJykpO1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjaGlsZC5zdGRvdXQhLm9uKCdkYXRhJywgY2h1bmsgPT4ge1xuICAgICAgb3B0aW9ucy5vdXRwdXQ/LndyaXRlKGNodW5rKTtcbiAgICAgIHN0ZG91dC5wdXNoKGNodW5rKTtcbiAgICAgIGV4ZWN1dGVBY3Rpb24oY2h1bmspO1xuICAgIH0pO1xuXG4gICAgY2hpbGQuc3RkZXJyIS5vbignZGF0YScsIGNodW5rID0+IHtcbiAgICAgIG9wdGlvbnMub3V0cHV0Py53cml0ZShjaHVuayk7XG4gICAgICBpZiAob3B0aW9ucy5jYXB0dXJlU3RkZXJyID8/IHRydWUpIHtcbiAgICAgICAgc3RkZXJyLnB1c2goY2h1bmspO1xuICAgICAgfVxuICAgICAgZXhlY3V0ZUFjdGlvbihjaHVuayk7XG4gICAgfSk7XG5cbiAgICBjaGlsZC5vbmNlKCdlcnJvcicsIHJlamVjdCk7XG5cbiAgICBjaGlsZC5vbmNlKCdjbG9zZScsIGNvZGUgPT4ge1xuICAgICAgY29uc3Qgb3V0cHV0ID0gKEJ1ZmZlci5jb25jYXQoc3Rkb3V0KS50b1N0cmluZygndXRmLTgnKSArIEJ1ZmZlci5jb25jYXQoc3RkZXJyKS50b1N0cmluZygndXRmLTgnKSkudHJpbSgpO1xuICAgICAgaWYgKGNvZGUgPT0gbnVsbCB8fCBjb2RlID09PSAwIHx8IG9wdGlvbnMuYWxsb3dFcnJFeGl0KSB7XG4gICAgICAgIGxldCByZXN1bHQgPSBuZXcgQXJyYXk8c3RyaW5nPigpO1xuICAgICAgICByZXN1bHQucHVzaChhY3Rpb25PdXRwdXQpO1xuICAgICAgICByZXN1bHQucHVzaChvdXRwdXQpO1xuICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICBhY3Rpb25TdWNjZWVkZWQ6IGFjdGlvblN1Y2NlZWRlZCxcbiAgICAgICAgICBhY3Rpb25PdXRwdXQ6IGFjdGlvbk91dHB1dCxcbiAgICAgICAgICBzaGVsbE91dHB1dDogb3V0cHV0LFxuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlamVjdChuZXcgRXJyb3IoYCcke2NvbW1hbmQuam9pbignICcpfScgZXhpdGVkIHdpdGggZXJyb3IgY29kZSAke2NvZGV9LiBPdXRwdXQ6IFxcbiR7b3V0cHV0fWApKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICB9KTtcbn1cblxuZnVuY3Rpb24ga2lsbFN1YlByb2Nlc3MoY2hpbGQ6IGNoaWxkX3Byb2Nlc3MuQ2hpbGRQcm9jZXNzLCBjb21tYW5kOiBzdHJpbmcpIHtcbiAgLyoqXG4gICAqIENoZWNrIGlmIHRoZSBzdWIgcHJvY2VzcyBpcyBydW5uaW5nIGluIGNvbnRhaW5lciwgc28gY2hpbGRfcHJvY2Vzcy5zcGF3biB3aWxsXG4gICAqIGNyZWF0ZSBtdWx0aXBsZSBwcm9jZXNzZXMsIGFuZCB0byBraWxsIGFsbCBvZiB0aGVtIHdlIG5lZWQgdG8gcnVuIGRpZmZlcmVudCBsb2dpY1xuICAgKi9cbiAgaWYgKGZzLmV4aXN0c1N5bmMoJy8uZG9ja2VyZW52JykpIHtcbiAgICBjaGlsZF9wcm9jZXNzLmV4ZWMoYGZvciBwaWQgaW4gJChwcyAtZWYgfCBncmVwIFwiJHtjb21tYW5kfVwiIHwgYXdrICd7cHJpbnQgJDJ9Jyk7IGRvIGtpbGwgLTIgJHBpZDsgZG9uZWApO1xuICB9IGVsc2Uge1xuICAgIGNoaWxkLmtpbGwoJ1NJR0lOVCcpO1xuICB9XG5cbn0iXX0=
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { TestContext } from './integ-test';
|
|
2
|
+
export interface TemporaryDirectoryContext {
|
|
3
|
+
readonly integTestDir: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function withTemporaryDirectory<A extends TestContext>(block: (context: A & TemporaryDirectoryContext) => Promise<void>): (context: A) => Promise<void>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withTemporaryDirectory = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const shell_1 = require("./shell");
|
|
8
|
+
function withTemporaryDirectory(block) {
|
|
9
|
+
return async (context) => {
|
|
10
|
+
const integTestDir = path.join(os.tmpdir(), `cdk-integ-${context.randomString}`);
|
|
11
|
+
fs.mkdirSync(integTestDir, { recursive: true });
|
|
12
|
+
try {
|
|
13
|
+
await block({
|
|
14
|
+
...context,
|
|
15
|
+
integTestDir,
|
|
16
|
+
});
|
|
17
|
+
// Clean up in case of success
|
|
18
|
+
if (process.env.SKIP_CLEANUP) {
|
|
19
|
+
context.log(`Left test directory in '${integTestDir}' ($SKIP_CLEANUP)\n`);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
shell_1.rimraf(integTestDir);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
context.log(`Left test directory in '${integTestDir}'\n`);
|
|
27
|
+
throw e;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
exports.withTemporaryDirectory = withTemporaryDirectory;
|
|
32
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aC10ZW1wb3JhcnktZGlyZWN0b3J5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsid2l0aC10ZW1wb3JhcnktZGlyZWN0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlCQUF5QjtBQUN6Qix5QkFBeUI7QUFDekIsNkJBQTZCO0FBRTdCLG1DQUFpQztBQU1qQyxTQUFnQixzQkFBc0IsQ0FBd0IsS0FBZ0U7SUFDNUgsT0FBTyxLQUFLLEVBQUUsT0FBVSxFQUFFLEVBQUU7UUFDMUIsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsYUFBYSxPQUFPLENBQUMsWUFBWSxFQUFFLENBQUMsQ0FBQztRQUVqRixFQUFFLENBQUMsU0FBUyxDQUFDLFlBQVksRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBRWhELElBQUk7WUFDRixNQUFNLEtBQUssQ0FBQztnQkFDVixHQUFHLE9BQU87Z0JBQ1YsWUFBWTthQUNiLENBQUMsQ0FBQztZQUVILDhCQUE4QjtZQUM5QixJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsWUFBWSxFQUFFO2dCQUM1QixPQUFPLENBQUMsR0FBRyxDQUFDLDJCQUEyQixZQUFZLHFCQUFxQixDQUFDLENBQUM7YUFDM0U7aUJBQU07Z0JBQ0wsY0FBTSxDQUFDLFlBQVksQ0FBQyxDQUFDO2FBQ3RCO1NBQ0Y7UUFBQyxPQUFPLENBQUMsRUFBRTtZQUNWLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLFlBQVksS0FBSyxDQUFDLENBQUM7WUFDMUQsTUFBTSxDQUFDLENBQUM7U0FDVDtJQUNILENBQUMsQ0FBQztBQUNKLENBQUM7QUF2QkQsd0RBdUJDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7IFRlc3RDb250ZXh0IH0gZnJvbSAnLi9pbnRlZy10ZXN0JztcbmltcG9ydCB7IHJpbXJhZiB9IGZyb20gJy4vc2hlbGwnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFRlbXBvcmFyeURpcmVjdG9yeUNvbnRleHQge1xuICByZWFkb25seSBpbnRlZ1Rlc3REaXI6IHN0cmluZztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHdpdGhUZW1wb3JhcnlEaXJlY3Rvcnk8QSBleHRlbmRzIFRlc3RDb250ZXh0PihibG9jazogKGNvbnRleHQ6IEEgJiBUZW1wb3JhcnlEaXJlY3RvcnlDb250ZXh0KSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gIHJldHVybiBhc3luYyAoY29udGV4dDogQSkgPT4ge1xuICAgIGNvbnN0IGludGVnVGVzdERpciA9IHBhdGguam9pbihvcy50bXBkaXIoKSwgYGNkay1pbnRlZy0ke2NvbnRleHQucmFuZG9tU3RyaW5nfWApO1xuXG4gICAgZnMubWtkaXJTeW5jKGludGVnVGVzdERpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgYmxvY2soe1xuICAgICAgICAuLi5jb250ZXh0LFxuICAgICAgICBpbnRlZ1Rlc3REaXIsXG4gICAgICB9KTtcblxuICAgICAgLy8gQ2xlYW4gdXAgaW4gY2FzZSBvZiBzdWNjZXNzXG4gICAgICBpZiAocHJvY2Vzcy5lbnYuU0tJUF9DTEVBTlVQKSB7XG4gICAgICAgIGNvbnRleHQubG9nKGBMZWZ0IHRlc3QgZGlyZWN0b3J5IGluICcke2ludGVnVGVzdERpcn0nICgkU0tJUF9DTEVBTlVQKVxcbmApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmltcmFmKGludGVnVGVzdERpcik7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29udGV4dC5sb2coYExlZnQgdGVzdCBkaXJlY3RvcnkgaW4gJyR7aW50ZWdUZXN0RGlyfSdcXG5gKTtcbiAgICAgIHRocm93IGU7XG4gICAgfVxuICB9O1xufVxuXG4iXX0=
|
package/lib/xpmutex.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export declare class XpMutexPool {
|
|
2
|
+
readonly directory: string;
|
|
3
|
+
static fromDirectory(directory: string): XpMutexPool;
|
|
4
|
+
static fromName(name: string): XpMutexPool;
|
|
5
|
+
private readonly waitingResolvers;
|
|
6
|
+
private watcher;
|
|
7
|
+
private constructor();
|
|
8
|
+
mutex(name: string): XpMutex;
|
|
9
|
+
/**
|
|
10
|
+
* Await an unlock event
|
|
11
|
+
*
|
|
12
|
+
* (An unlock event is when a file in the directory gets deleted, with a tiny
|
|
13
|
+
* random sleep attached to it).
|
|
14
|
+
*/
|
|
15
|
+
awaitUnlock(maxWaitMs?: number): Promise<void>;
|
|
16
|
+
private startWatch;
|
|
17
|
+
private notifyWaiters;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Cross-process mutex
|
|
21
|
+
*
|
|
22
|
+
* Uses the presence of a file on disk and `fs.watch` to represent the mutex
|
|
23
|
+
* and discover unlocks.
|
|
24
|
+
*/
|
|
25
|
+
export declare class XpMutex {
|
|
26
|
+
private readonly pool;
|
|
27
|
+
readonly mutexName: string;
|
|
28
|
+
private readonly fileName;
|
|
29
|
+
constructor(pool: XpMutexPool, mutexName: string);
|
|
30
|
+
/**
|
|
31
|
+
* Try to acquire the lock (may fail)
|
|
32
|
+
*/
|
|
33
|
+
tryAcquire(): Promise<ILock | undefined>;
|
|
34
|
+
/**
|
|
35
|
+
* Acquire the lock, waiting until we can
|
|
36
|
+
*/
|
|
37
|
+
acquire(): Promise<ILock>;
|
|
38
|
+
private readPidFile;
|
|
39
|
+
private writePidFile;
|
|
40
|
+
}
|
|
41
|
+
export interface ILock {
|
|
42
|
+
release(): Promise<void>;
|
|
43
|
+
}
|
package/lib/xpmutex.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.XpMutex = exports.XpMutexPool = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
class XpMutexPool {
|
|
8
|
+
constructor(directory) {
|
|
9
|
+
this.directory = directory;
|
|
10
|
+
this.waitingResolvers = new Set();
|
|
11
|
+
this.startWatch();
|
|
12
|
+
}
|
|
13
|
+
static fromDirectory(directory) {
|
|
14
|
+
fs_1.mkdirSync(directory, { recursive: true });
|
|
15
|
+
return new XpMutexPool(directory);
|
|
16
|
+
}
|
|
17
|
+
static fromName(name) {
|
|
18
|
+
return XpMutexPool.fromDirectory(path.join(os.tmpdir(), name));
|
|
19
|
+
}
|
|
20
|
+
mutex(name) {
|
|
21
|
+
return new XpMutex(this, name);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Await an unlock event
|
|
25
|
+
*
|
|
26
|
+
* (An unlock event is when a file in the directory gets deleted, with a tiny
|
|
27
|
+
* random sleep attached to it).
|
|
28
|
+
*/
|
|
29
|
+
awaitUnlock(maxWaitMs) {
|
|
30
|
+
const wait = new Promise(ok => {
|
|
31
|
+
this.waitingResolvers.add(async () => {
|
|
32
|
+
await randomSleep(10);
|
|
33
|
+
ok();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
if (maxWaitMs) {
|
|
37
|
+
return Promise.race([wait, sleep(maxWaitMs)]);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
return wait;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
startWatch() {
|
|
44
|
+
this.watcher = fs_1.watch(this.directory);
|
|
45
|
+
this.watcher.unref(); // @types doesn't know about this but it exists
|
|
46
|
+
this.watcher.on('change', async (eventType, fname) => {
|
|
47
|
+
// Only trigger on 'deletes'.
|
|
48
|
+
// After receiving the event, we check if the file exists.
|
|
49
|
+
// - If no: the file was deleted! Huzzah, this counts as a wakeup.
|
|
50
|
+
// - If yes: either the file was just created (in which case we don't need to wakeup)
|
|
51
|
+
// or the event was due to a delete but someone raced us to it and claimed the
|
|
52
|
+
// file already (in which case we also don't need to wake up).
|
|
53
|
+
if (eventType === 'rename' && !await fileExists(path.join(this.directory, fname.toString()))) {
|
|
54
|
+
this.notifyWaiters();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
this.watcher.on('error', async (e) => {
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
59
|
+
console.error(e);
|
|
60
|
+
await randomSleep(100);
|
|
61
|
+
this.startWatch();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
notifyWaiters() {
|
|
65
|
+
for (const promise of this.waitingResolvers) {
|
|
66
|
+
promise();
|
|
67
|
+
}
|
|
68
|
+
this.waitingResolvers.clear();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.XpMutexPool = XpMutexPool;
|
|
72
|
+
/**
|
|
73
|
+
* Cross-process mutex
|
|
74
|
+
*
|
|
75
|
+
* Uses the presence of a file on disk and `fs.watch` to represent the mutex
|
|
76
|
+
* and discover unlocks.
|
|
77
|
+
*/
|
|
78
|
+
class XpMutex {
|
|
79
|
+
constructor(pool, mutexName) {
|
|
80
|
+
this.pool = pool;
|
|
81
|
+
this.mutexName = mutexName;
|
|
82
|
+
this.fileName = path.join(pool.directory, `${mutexName}.mutex`);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Try to acquire the lock (may fail)
|
|
86
|
+
*/
|
|
87
|
+
async tryAcquire() {
|
|
88
|
+
while (true) {
|
|
89
|
+
// Acquire lock by being the one to create the file
|
|
90
|
+
try {
|
|
91
|
+
return await this.writePidFile('wx'); // Fails if the file already exists
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
if (e.code !== 'EEXIST') {
|
|
95
|
+
throw e;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// File already exists. Read the contents, see if it's an existent PID (if so, the lock is taken)
|
|
99
|
+
const ownerPid = await this.readPidFile();
|
|
100
|
+
if (ownerPid === undefined) {
|
|
101
|
+
// File got deleted just now, maybe we can acquire it again
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (processExists(ownerPid)) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
// If not, the lock is stale and will never be released anymore. We may
|
|
108
|
+
// delete it and acquire it anyway, but we may be racing someone else trying
|
|
109
|
+
// to do the same. Solve this as follows:
|
|
110
|
+
// - Try to acquire a lock that gives us permissions to declare the existing lock stale.
|
|
111
|
+
// - Sleep a small random period to reduce contention on this operation
|
|
112
|
+
await randomSleep(10);
|
|
113
|
+
const innerMux = new XpMutex(this.pool, `${this.mutexName}.${ownerPid}`);
|
|
114
|
+
const innerLock = await innerMux.tryAcquire();
|
|
115
|
+
if (!innerLock) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
// We may not release the 'inner lock' we used to acquire the rights to declare the other
|
|
119
|
+
// lock stale until we release the actual lock itself. If we did, other contenders might
|
|
120
|
+
// see it released while they're still in this fallback block and accidentally steal
|
|
121
|
+
// from a new legitimate owner.
|
|
122
|
+
return this.writePidFile('w', innerLock); // Force write lock file, attach inner lock as well
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Acquire the lock, waiting until we can
|
|
127
|
+
*/
|
|
128
|
+
async acquire() {
|
|
129
|
+
while (true) {
|
|
130
|
+
// Start the wait here, so we don't miss the signal if it comes after
|
|
131
|
+
// we try but before we sleep.
|
|
132
|
+
//
|
|
133
|
+
// We also periodically retry anyway since we may have missed the delete
|
|
134
|
+
// signal due to unfortunate timing.
|
|
135
|
+
const wait = this.pool.awaitUnlock(5000);
|
|
136
|
+
const lock = await this.acquire();
|
|
137
|
+
if (lock) {
|
|
138
|
+
// Ignore the wait (count as handled)
|
|
139
|
+
wait.then(() => { }, () => { });
|
|
140
|
+
return lock;
|
|
141
|
+
}
|
|
142
|
+
await wait;
|
|
143
|
+
await randomSleep(100);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async readPidFile() {
|
|
147
|
+
const deadLine = Date.now() + 1000;
|
|
148
|
+
while (Date.now() < deadLine) {
|
|
149
|
+
let contents;
|
|
150
|
+
try {
|
|
151
|
+
contents = await fs_1.promises.readFile(this.fileName, { encoding: 'utf-8' });
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
if (e.code === 'ENOENT') {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
throw e;
|
|
158
|
+
}
|
|
159
|
+
// Retry until we've seen the full contents
|
|
160
|
+
if (contents.endsWith('.')) {
|
|
161
|
+
return parseInt(contents.substring(0, contents.length - 1), 10);
|
|
162
|
+
}
|
|
163
|
+
await sleep(10);
|
|
164
|
+
}
|
|
165
|
+
throw new Error(`${this.fileName} was never completely written`);
|
|
166
|
+
}
|
|
167
|
+
async writePidFile(mode, additionalLock) {
|
|
168
|
+
const fd = await fs_1.promises.open(this.fileName, mode); // May fail if the file already exists
|
|
169
|
+
await fd.write(`${process.pid}.`); // Period guards against partial reads
|
|
170
|
+
await fd.close();
|
|
171
|
+
return {
|
|
172
|
+
release: async () => {
|
|
173
|
+
await fs_1.promises.unlink(this.fileName);
|
|
174
|
+
await additionalLock?.release();
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
exports.XpMutex = XpMutex;
|
|
180
|
+
async function fileExists(fileName) {
|
|
181
|
+
try {
|
|
182
|
+
await fs_1.promises.stat(fileName);
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
if (e.code === 'ENOENT') {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
throw e;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function processExists(pid) {
|
|
193
|
+
try {
|
|
194
|
+
process.kill(pid, 0);
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function sleep(ms) {
|
|
202
|
+
return new Promise(ok => setTimeout(ok, ms).unref());
|
|
203
|
+
}
|
|
204
|
+
function randomSleep(ms) {
|
|
205
|
+
return sleep(Math.floor(Math.random() * ms));
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoieHBtdXRleC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInhwbXV0ZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMkJBQXNEO0FBQ3RELHlCQUF5QjtBQUN6Qiw2QkFBNkI7QUFFN0IsTUFBYSxXQUFXO0lBYXRCLFlBQW9DLFNBQWlCO1FBQWpCLGNBQVMsR0FBVCxTQUFTLENBQVE7UUFIcEMscUJBQWdCLEdBQUcsSUFBSSxHQUFHLEVBQWMsQ0FBQztRQUl4RCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDcEIsQ0FBQztJQWRNLE1BQU0sQ0FBQyxhQUFhLENBQUMsU0FBaUI7UUFDM0MsY0FBUyxDQUFDLFNBQVMsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzFDLE9BQU8sSUFBSSxXQUFXLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDcEMsQ0FBQztJQUVNLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBWTtRQUNqQyxPQUFPLFdBQVcsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUNqRSxDQUFDO0lBU00sS0FBSyxDQUFDLElBQVk7UUFDdkIsT0FBTyxJQUFJLE9BQU8sQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDakMsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ksV0FBVyxDQUFDLFNBQWtCO1FBQ25DLE1BQU0sSUFBSSxHQUFHLElBQUksT0FBTyxDQUFPLEVBQUUsQ0FBQyxFQUFFO1lBQ2xDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsS0FBSyxJQUFJLEVBQUU7Z0JBQ25DLE1BQU0sV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUN0QixFQUFFLEVBQUUsQ0FBQztZQUNQLENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQyxDQUFDLENBQUM7UUFFSCxJQUFJLFNBQVMsRUFBRTtZQUNiLE9BQU8sT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQy9DO2FBQU07WUFDTCxPQUFPLElBQUksQ0FBQztTQUNiO0lBQ0gsQ0FBQztJQUVPLFVBQVU7UUFDaEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxVQUFLLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ3BDLElBQUksQ0FBQyxPQUFlLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQywrQ0FBK0M7UUFDOUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsUUFBUSxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLEVBQUU7WUFDbkQsNkJBQTZCO1lBQzdCLDBEQUEwRDtZQUMxRCxrRUFBa0U7WUFDbEUscUZBQXFGO1lBQ3JGLGdGQUFnRjtZQUNoRixnRUFBZ0U7WUFDaEUsSUFBSSxTQUFTLEtBQUssUUFBUSxJQUFJLENBQUMsTUFBTSxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLEtBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLEVBQUU7Z0JBQzVGLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQzthQUN0QjtRQUNILENBQUMsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUNuQyxzQ0FBc0M7WUFDdEMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNqQixNQUFNLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN2QixJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDcEIsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU8sYUFBYTtRQUNuQixLQUFLLE1BQU0sT0FBTyxJQUFJLElBQUksQ0FBQyxnQkFBZ0IsRUFBRTtZQUMzQyxPQUFPLEVBQUUsQ0FBQztTQUNYO1FBQ0QsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ2hDLENBQUM7Q0FDRjtBQXRFRCxrQ0FzRUM7QUFFRDs7Ozs7R0FLRztBQUNILE1BQWEsT0FBTztJQUdsQixZQUE2QixJQUFpQixFQUFrQixTQUFpQjtRQUFwRCxTQUFJLEdBQUosSUFBSSxDQUFhO1FBQWtCLGNBQVMsR0FBVCxTQUFTLENBQVE7UUFDL0UsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsR0FBRyxTQUFTLFFBQVEsQ0FBQyxDQUFDO0lBQ2xFLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxVQUFVO1FBQ3JCLE9BQU8sSUFBSSxFQUFFO1lBQ1gsbURBQW1EO1lBQ25ELElBQUk7Z0JBQ0YsT0FBTyxNQUFNLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxtQ0FBbUM7YUFDMUU7WUFBQyxPQUFPLENBQUMsRUFBRTtnQkFDVixJQUFJLENBQUMsQ0FBQyxJQUFJLEtBQUssUUFBUSxFQUFFO29CQUFFLE1BQU0sQ0FBQyxDQUFDO2lCQUFFO2FBQ3RDO1lBRUQsaUdBQWlHO1lBQ2pHLE1BQU0sUUFBUSxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQzFDLElBQUksUUFBUSxLQUFLLFNBQVMsRUFBRTtnQkFDMUIsMkRBQTJEO2dCQUMzRCxTQUFTO2FBQ1Y7WUFDRCxJQUFJLGFBQWEsQ0FBQyxRQUFRLENBQUMsRUFBRTtnQkFDM0IsT0FBTyxTQUFTLENBQUM7YUFDbEI7WUFFRCx1RUFBdUU7WUFDdkUsNEVBQTRFO1lBQzVFLHlDQUF5QztZQUN6Qyx3RkFBd0Y7WUFDeEYsdUVBQXVFO1lBQ3ZFLE1BQU0sV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ3RCLE1BQU0sUUFBUSxHQUFHLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsR0FBRyxJQUFJLENBQUMsU0FBUyxJQUFJLFFBQVEsRUFBRSxDQUFDLENBQUM7WUFDekUsTUFBTSxTQUFTLEdBQUcsTUFBTSxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDOUMsSUFBSSxDQUFDLFNBQVMsRUFBRTtnQkFDZCxPQUFPLFNBQVMsQ0FBQzthQUNsQjtZQUVELHlGQUF5RjtZQUN6Rix3RkFBd0Y7WUFDeEYsb0ZBQW9GO1lBQ3BGLCtCQUErQjtZQUMvQixPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsR0FBRyxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUMsbURBQW1EO1NBQzlGO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLE9BQU87UUFDbEIsT0FBTyxJQUFJLEVBQUU7WUFDWCxxRUFBcUU7WUFDckUsOEJBQThCO1lBQzlCLEVBQUU7WUFDRix3RUFBd0U7WUFDeEUsb0NBQW9DO1lBQ3BDLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBRXpDLE1BQU0sSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2xDLElBQUksSUFBSSxFQUFFO2dCQUNSLHFDQUFxQztnQkFDckMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLEdBQUUsQ0FBQyxDQUFDLENBQUM7Z0JBQzlCLE9BQU8sSUFBSSxDQUFDO2FBQ2I7WUFFRCxNQUFNLElBQUksQ0FBQztZQUNYLE1BQU0sV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1NBQ3hCO0lBQ0gsQ0FBQztJQUVPLEtBQUssQ0FBQyxXQUFXO1FBQ3ZCLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7UUFDbkMsT0FBTyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsUUFBUSxFQUFFO1lBQzVCLElBQUksUUFBUSxDQUFDO1lBQ2IsSUFBSTtnQkFDRixRQUFRLEdBQUcsTUFBTSxhQUFFLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQzthQUNwRTtZQUFDLE9BQU8sQ0FBQyxFQUFFO2dCQUNWLElBQUksQ0FBQyxDQUFDLElBQUksS0FBSyxRQUFRLEVBQUU7b0JBQUUsT0FBTyxTQUFTLENBQUM7aUJBQUU7Z0JBQzlDLE1BQU0sQ0FBQyxDQUFDO2FBQ1Q7WUFFRCwyQ0FBMkM7WUFDM0MsSUFBSSxRQUFRLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFO2dCQUFFLE9BQU8sUUFBUSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7YUFBRTtZQUNoRyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztTQUNqQjtRQUVELE1BQU0sSUFBSSxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsUUFBUSwrQkFBK0IsQ0FBQyxDQUFDO0lBQ25FLENBQUM7SUFFTyxLQUFLLENBQUMsWUFBWSxDQUFDLElBQVksRUFBRSxjQUFzQjtRQUM3RCxNQUFNLEVBQUUsR0FBRyxNQUFNLGFBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLHNDQUFzQztRQUNyRixNQUFNLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxPQUFPLENBQUMsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLHNDQUFzQztRQUN6RSxNQUFNLEVBQUUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUVqQixPQUFPO1lBQ0wsT0FBTyxFQUFFLEtBQUssSUFBSSxFQUFFO2dCQUNsQixNQUFNLGFBQUUsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO2dCQUMvQixNQUFNLGNBQWMsRUFBRSxPQUFPLEVBQUUsQ0FBQztZQUNsQyxDQUFDO1NBQ0YsQ0FBQztJQUNKLENBQUM7Q0FDRjtBQXhHRCwwQkF3R0M7QUFNRCxLQUFLLFVBQVUsVUFBVSxDQUFDLFFBQWdCO0lBQ3hDLElBQUk7UUFDRixNQUFNLGFBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDeEIsT0FBTyxJQUFJLENBQUM7S0FDYjtJQUFDLE9BQU8sQ0FBQyxFQUFFO1FBQ1YsSUFBSSxDQUFDLENBQUMsSUFBSSxLQUFLLFFBQVEsRUFBRTtZQUFFLE9BQU8sS0FBSyxDQUFDO1NBQUU7UUFDMUMsTUFBTSxDQUFDLENBQUM7S0FDVDtBQUNILENBQUM7QUFFRCxTQUFTLGFBQWEsQ0FBQyxHQUFXO0lBQ2hDLElBQUk7UUFDRixPQUFPLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNyQixPQUFPLElBQUksQ0FBQztLQUNiO0lBQUMsT0FBTyxDQUFDLEVBQUU7UUFDVixPQUFPLEtBQUssQ0FBQztLQUNkO0FBQ0gsQ0FBQztBQUVELFNBQVMsS0FBSyxDQUFDLEVBQVU7SUFDdkIsT0FBTyxJQUFJLE9BQU8sQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFFLFVBQVUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUFTLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQztBQUNoRSxDQUFDO0FBRUQsU0FBUyxXQUFXLENBQUMsRUFBVTtJQUM3QixPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBQy9DLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyB3YXRjaCwgcHJvbWlzZXMgYXMgZnMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIG9zIGZyb20gJ29zJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5cbmV4cG9ydCBjbGFzcyBYcE11dGV4UG9vbCB7XG4gIHB1YmxpYyBzdGF0aWMgZnJvbURpcmVjdG9yeShkaXJlY3Rvcnk6IHN0cmluZykge1xuICAgIG1rZGlyU3luYyhkaXJlY3RvcnksIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgIHJldHVybiBuZXcgWHBNdXRleFBvb2woZGlyZWN0b3J5KTtcbiAgfVxuXG4gIHB1YmxpYyBzdGF0aWMgZnJvbU5hbWUobmFtZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIFhwTXV0ZXhQb29sLmZyb21EaXJlY3RvcnkocGF0aC5qb2luKG9zLnRtcGRpcigpLCBuYW1lKSk7XG4gIH1cblxuICBwcml2YXRlIHJlYWRvbmx5IHdhaXRpbmdSZXNvbHZlcnMgPSBuZXcgU2V0PCgpID0+IHZvaWQ+KCk7XG4gIHByaXZhdGUgd2F0Y2hlcjogUmV0dXJuVHlwZTx0eXBlb2Ygd2F0Y2g+IHwgdW5kZWZpbmVkO1xuXG4gIHByaXZhdGUgY29uc3RydWN0b3IocHVibGljIHJlYWRvbmx5IGRpcmVjdG9yeTogc3RyaW5nKSB7XG4gICAgdGhpcy5zdGFydFdhdGNoKCk7XG4gIH1cblxuICBwdWJsaWMgbXV0ZXgobmFtZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIG5ldyBYcE11dGV4KHRoaXMsIG5hbWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIEF3YWl0IGFuIHVubG9jayBldmVudFxuICAgKlxuICAgKiAoQW4gdW5sb2NrIGV2ZW50IGlzIHdoZW4gYSBmaWxlIGluIHRoZSBkaXJlY3RvcnkgZ2V0cyBkZWxldGVkLCB3aXRoIGEgdGlueVxuICAgKiByYW5kb20gc2xlZXAgYXR0YWNoZWQgdG8gaXQpLlxuICAgKi9cbiAgcHVibGljIGF3YWl0VW5sb2NrKG1heFdhaXRNcz86IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHdhaXQgPSBuZXcgUHJvbWlzZTx2b2lkPihvayA9PiB7XG4gICAgICB0aGlzLndhaXRpbmdSZXNvbHZlcnMuYWRkKGFzeW5jICgpID0+IHtcbiAgICAgICAgYXdhaXQgcmFuZG9tU2xlZXAoMTApO1xuICAgICAgICBvaygpO1xuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICBpZiAobWF4V2FpdE1zKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yYWNlKFt3YWl0LCBzbGVlcChtYXhXYWl0TXMpXSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiB3YWl0O1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgc3RhcnRXYXRjaCgpIHtcbiAgICB0aGlzLndhdGNoZXIgPSB3YXRjaCh0aGlzLmRpcmVjdG9yeSk7XG4gICAgKHRoaXMud2F0Y2hlciBhcyBhbnkpLnVucmVmKCk7IC8vIEB0eXBlcyBkb2Vzbid0IGtub3cgYWJvdXQgdGhpcyBidXQgaXQgZXhpc3RzXG4gICAgdGhpcy53YXRjaGVyLm9uKCdjaGFuZ2UnLCBhc3luYyAoZXZlbnRUeXBlLCBmbmFtZSkgPT4ge1xuICAgICAgLy8gT25seSB0cmlnZ2VyIG9uICdkZWxldGVzJy5cbiAgICAgIC8vIEFmdGVyIHJlY2VpdmluZyB0aGUgZXZlbnQsIHdlIGNoZWNrIGlmIHRoZSBmaWxlIGV4aXN0cy5cbiAgICAgIC8vIC0gSWYgbm86IHRoZSBmaWxlIHdhcyBkZWxldGVkISBIdXp6YWgsIHRoaXMgY291bnRzIGFzIGEgd2FrZXVwLlxuICAgICAgLy8gLSBJZiB5ZXM6IGVpdGhlciB0aGUgZmlsZSB3YXMganVzdCBjcmVhdGVkIChpbiB3aGljaCBjYXNlIHdlIGRvbid0IG5lZWQgdG8gd2FrZXVwKVxuICAgICAgLy8gICBvciB0aGUgZXZlbnQgd2FzIGR1ZSB0byBhIGRlbGV0ZSBidXQgc29tZW9uZSByYWNlZCB1cyB0byBpdCBhbmQgY2xhaW1lZCB0aGVcbiAgICAgIC8vICAgZmlsZSBhbHJlYWR5IChpbiB3aGljaCBjYXNlIHdlIGFsc28gZG9uJ3QgbmVlZCB0byB3YWtlIHVwKS5cbiAgICAgIGlmIChldmVudFR5cGUgPT09ICdyZW5hbWUnICYmICFhd2FpdCBmaWxlRXhpc3RzKHBhdGguam9pbih0aGlzLmRpcmVjdG9yeSwgZm5hbWUudG9TdHJpbmcoKSkpKSB7XG4gICAgICAgIHRoaXMubm90aWZ5V2FpdGVycygpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMud2F0Y2hlci5vbignZXJyb3InLCBhc3luYyAoZSkgPT4ge1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnNvbGVcbiAgICAgIGNvbnNvbGUuZXJyb3IoZSk7XG4gICAgICBhd2FpdCByYW5kb21TbGVlcCgxMDApO1xuICAgICAgdGhpcy5zdGFydFdhdGNoKCk7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIG5vdGlmeVdhaXRlcnMoKSB7XG4gICAgZm9yIChjb25zdCBwcm9taXNlIG9mIHRoaXMud2FpdGluZ1Jlc29sdmVycykge1xuICAgICAgcHJvbWlzZSgpO1xuICAgIH1cbiAgICB0aGlzLndhaXRpbmdSZXNvbHZlcnMuY2xlYXIoKTtcbiAgfVxufVxuXG4vKipcbiAqIENyb3NzLXByb2Nlc3MgbXV0ZXhcbiAqXG4gKiBVc2VzIHRoZSBwcmVzZW5jZSBvZiBhIGZpbGUgb24gZGlzayBhbmQgYGZzLndhdGNoYCB0byByZXByZXNlbnQgdGhlIG11dGV4XG4gKiBhbmQgZGlzY292ZXIgdW5sb2Nrcy5cbiAqL1xuZXhwb3J0IGNsYXNzIFhwTXV0ZXgge1xuICBwcml2YXRlIHJlYWRvbmx5IGZpbGVOYW1lOiBzdHJpbmc7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBwb29sOiBYcE11dGV4UG9vbCwgcHVibGljIHJlYWRvbmx5IG11dGV4TmFtZTogc3RyaW5nKSB7XG4gICAgdGhpcy5maWxlTmFtZSA9IHBhdGguam9pbihwb29sLmRpcmVjdG9yeSwgYCR7bXV0ZXhOYW1lfS5tdXRleGApO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyeSB0byBhY3F1aXJlIHRoZSBsb2NrIChtYXkgZmFpbClcbiAgICovXG4gIHB1YmxpYyBhc3luYyB0cnlBY3F1aXJlKCk6IFByb21pc2U8SUxvY2sgfCB1bmRlZmluZWQ+IHtcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgLy8gQWNxdWlyZSBsb2NrIGJ5IGJlaW5nIHRoZSBvbmUgdG8gY3JlYXRlIHRoZSBmaWxlXG4gICAgICB0cnkge1xuICAgICAgICByZXR1cm4gYXdhaXQgdGhpcy53cml0ZVBpZEZpbGUoJ3d4Jyk7IC8vIEZhaWxzIGlmIHRoZSBmaWxlIGFscmVhZHkgZXhpc3RzXG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGlmIChlLmNvZGUgIT09ICdFRVhJU1QnKSB7IHRocm93IGU7IH1cbiAgICAgIH1cblxuICAgICAgLy8gRmlsZSBhbHJlYWR5IGV4aXN0cy4gUmVhZCB0aGUgY29udGVudHMsIHNlZSBpZiBpdCdzIGFuIGV4aXN0ZW50IFBJRCAoaWYgc28sIHRoZSBsb2NrIGlzIHRha2VuKVxuICAgICAgY29uc3Qgb3duZXJQaWQgPSBhd2FpdCB0aGlzLnJlYWRQaWRGaWxlKCk7XG4gICAgICBpZiAob3duZXJQaWQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAvLyBGaWxlIGdvdCBkZWxldGVkIGp1c3Qgbm93LCBtYXliZSB3ZSBjYW4gYWNxdWlyZSBpdCBhZ2FpblxuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGlmIChwcm9jZXNzRXhpc3RzKG93bmVyUGlkKSkge1xuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgfVxuXG4gICAgICAvLyBJZiBub3QsIHRoZSBsb2NrIGlzIHN0YWxlIGFuZCB3aWxsIG5ldmVyIGJlIHJlbGVhc2VkIGFueW1vcmUuIFdlIG1heVxuICAgICAgLy8gZGVsZXRlIGl0IGFuZCBhY3F1aXJlIGl0IGFueXdheSwgYnV0IHdlIG1heSBiZSByYWNpbmcgc29tZW9uZSBlbHNlIHRyeWluZ1xuICAgICAgLy8gdG8gZG8gdGhlIHNhbWUuIFNvbHZlIHRoaXMgYXMgZm9sbG93czpcbiAgICAgIC8vIC0gVHJ5IHRvIGFjcXVpcmUgYSBsb2NrIHRoYXQgZ2l2ZXMgdXMgcGVybWlzc2lvbnMgdG8gZGVjbGFyZSB0aGUgZXhpc3RpbmcgbG9jayBzdGFsZS5cbiAgICAgIC8vIC0gU2xlZXAgYSBzbWFsbCByYW5kb20gcGVyaW9kIHRvIHJlZHVjZSBjb250ZW50aW9uIG9uIHRoaXMgb3BlcmF0aW9uXG4gICAgICBhd2FpdCByYW5kb21TbGVlcCgxMCk7XG4gICAgICBjb25zdCBpbm5lck11eCA9IG5ldyBYcE11dGV4KHRoaXMucG9vbCwgYCR7dGhpcy5tdXRleE5hbWV9LiR7b3duZXJQaWR9YCk7XG4gICAgICBjb25zdCBpbm5lckxvY2sgPSBhd2FpdCBpbm5lck11eC50cnlBY3F1aXJlKCk7XG4gICAgICBpZiAoIWlubmVyTG9jaykge1xuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgICAgfVxuXG4gICAgICAvLyBXZSBtYXkgbm90IHJlbGVhc2UgdGhlICdpbm5lciBsb2NrJyB3ZSB1c2VkIHRvIGFjcXVpcmUgdGhlIHJpZ2h0cyB0byBkZWNsYXJlIHRoZSBvdGhlclxuICAgICAgLy8gbG9jayBzdGFsZSB1bnRpbCB3ZSByZWxlYXNlIHRoZSBhY3R1YWwgbG9jayBpdHNlbGYuIElmIHdlIGRpZCwgb3RoZXIgY29udGVuZGVycyBtaWdodFxuICAgICAgLy8gc2VlIGl0IHJlbGVhc2VkIHdoaWxlIHRoZXkncmUgc3RpbGwgaW4gdGhpcyBmYWxsYmFjayBibG9jayBhbmQgYWNjaWRlbnRhbGx5IHN0ZWFsXG4gICAgICAvLyBmcm9tIGEgbmV3IGxlZ2l0aW1hdGUgb3duZXIuXG4gICAgICByZXR1cm4gdGhpcy53cml0ZVBpZEZpbGUoJ3cnLCBpbm5lckxvY2spOyAvLyBGb3JjZSB3cml0ZSBsb2NrIGZpbGUsIGF0dGFjaCBpbm5lciBsb2NrIGFzIHdlbGxcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQWNxdWlyZSB0aGUgbG9jaywgd2FpdGluZyB1bnRpbCB3ZSBjYW5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBhY3F1aXJlKCk6IFByb21pc2U8SUxvY2s+IHtcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgLy8gU3RhcnQgdGhlIHdhaXQgaGVyZSwgc28gd2UgZG9uJ3QgbWlzcyB0aGUgc2lnbmFsIGlmIGl0IGNvbWVzIGFmdGVyXG4gICAgICAvLyB3ZSB0cnkgYnV0IGJlZm9yZSB3ZSBzbGVlcC5cbiAgICAgIC8vXG4gICAgICAvLyBXZSBhbHNvIHBlcmlvZGljYWxseSByZXRyeSBhbnl3YXkgc2luY2Ugd2UgbWF5IGhhdmUgbWlzc2VkIHRoZSBkZWxldGVcbiAgICAgIC8vIHNpZ25hbCBkdWUgdG8gdW5mb3J0dW5hdGUgdGltaW5nLlxuICAgICAgY29uc3Qgd2FpdCA9IHRoaXMucG9vbC5hd2FpdFVubG9jayg1MDAwKTtcblxuICAgICAgY29uc3QgbG9jayA9IGF3YWl0IHRoaXMuYWNxdWlyZSgpO1xuICAgICAgaWYgKGxvY2spIHtcbiAgICAgICAgLy8gSWdub3JlIHRoZSB3YWl0IChjb3VudCBhcyBoYW5kbGVkKVxuICAgICAgICB3YWl0LnRoZW4oKCkgPT4ge30sICgpID0+IHt9KTtcbiAgICAgICAgcmV0dXJuIGxvY2s7XG4gICAgICB9XG5cbiAgICAgIGF3YWl0IHdhaXQ7XG4gICAgICBhd2FpdCByYW5kb21TbGVlcCgxMDApO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcmVhZFBpZEZpbGUoKTogUHJvbWlzZTxudW1iZXIgfCB1bmRlZmluZWQ+IHtcbiAgICBjb25zdCBkZWFkTGluZSA9IERhdGUubm93KCkgKyAxMDAwO1xuICAgIHdoaWxlIChEYXRlLm5vdygpIDwgZGVhZExpbmUpIHtcbiAgICAgIGxldCBjb250ZW50cztcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnRlbnRzID0gYXdhaXQgZnMucmVhZEZpbGUodGhpcy5maWxlTmFtZSwgeyBlbmNvZGluZzogJ3V0Zi04JyB9KTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgaWYgKGUuY29kZSA9PT0gJ0VOT0VOVCcpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfVxuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuXG4gICAgICAvLyBSZXRyeSB1bnRpbCB3ZSd2ZSBzZWVuIHRoZSBmdWxsIGNvbnRlbnRzXG4gICAgICBpZiAoY29udGVudHMuZW5kc1dpdGgoJy4nKSkgeyByZXR1cm4gcGFyc2VJbnQoY29udGVudHMuc3Vic3RyaW5nKDAsIGNvbnRlbnRzLmxlbmd0aCAtIDEpLCAxMCk7IH1cbiAgICAgIGF3YWl0IHNsZWVwKDEwKTtcbiAgICB9XG5cbiAgICB0aHJvdyBuZXcgRXJyb3IoYCR7dGhpcy5maWxlTmFtZX0gd2FzIG5ldmVyIGNvbXBsZXRlbHkgd3JpdHRlbmApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyB3cml0ZVBpZEZpbGUobW9kZTogc3RyaW5nLCBhZGRpdGlvbmFsTG9jaz86IElMb2NrKTogUHJvbWlzZTxJTG9jaz4ge1xuICAgIGNvbnN0IGZkID0gYXdhaXQgZnMub3Blbih0aGlzLmZpbGVOYW1lLCBtb2RlKTsgLy8gTWF5IGZhaWwgaWYgdGhlIGZpbGUgYWxyZWFkeSBleGlzdHNcbiAgICBhd2FpdCBmZC53cml0ZShgJHtwcm9jZXNzLnBpZH0uYCk7IC8vIFBlcmlvZCBndWFyZHMgYWdhaW5zdCBwYXJ0aWFsIHJlYWRzXG4gICAgYXdhaXQgZmQuY2xvc2UoKTtcblxuICAgIHJldHVybiB7XG4gICAgICByZWxlYXNlOiBhc3luYyAoKSA9PiB7XG4gICAgICAgIGF3YWl0IGZzLnVubGluayh0aGlzLmZpbGVOYW1lKTtcbiAgICAgICAgYXdhaXQgYWRkaXRpb25hbExvY2s/LnJlbGVhc2UoKTtcbiAgICAgIH0sXG4gICAgfTtcbiAgfVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIElMb2NrIHtcbiAgcmVsZWFzZSgpOiBQcm9taXNlPHZvaWQ+O1xufVxuXG5hc3luYyBmdW5jdGlvbiBmaWxlRXhpc3RzKGZpbGVOYW1lOiBzdHJpbmcpIHtcbiAgdHJ5IHtcbiAgICBhd2FpdCBmcy5zdGF0KGZpbGVOYW1lKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGlmIChlLmNvZGUgPT09ICdFTk9FTlQnKSB7IHJldHVybiBmYWxzZTsgfVxuICAgIHRocm93IGU7XG4gIH1cbn1cblxuZnVuY3Rpb24gcHJvY2Vzc0V4aXN0cyhwaWQ6IG51bWJlcikge1xuICB0cnkge1xuICAgIHByb2Nlc3Mua2lsbChwaWQsIDApO1xuICAgIHJldHVybiB0cnVlO1xuICB9IGNhdGNoIChlKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbmZ1bmN0aW9uIHNsZWVwKG1zOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKG9rID0+IChzZXRUaW1lb3V0KG9rLCBtcykgYXMgYW55KS51bnJlZigpKTtcbn1cblxuZnVuY3Rpb24gcmFuZG9tU2xlZXAobXM6IG51bWJlcikge1xuICByZXR1cm4gc2xlZXAoTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogbXMpKTtcbn1cbiJdfQ==
|