@gatling.io/cli 3.11.7 → 3.13.1

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 (63) hide show
  1. package/package.json +8 -6
  2. package/polyfills/crypto.js +87 -0
  3. package/polyfills/global.js +13 -0
  4. package/src/bundle/index.ts +43 -0
  5. package/src/bundle/polyfill.ts +91 -0
  6. package/src/commands/build.ts +34 -0
  7. package/src/commands/enterpriseDeploy.ts +97 -0
  8. package/src/commands/enterprisePackage.ts +45 -0
  9. package/src/commands/enterpriseStart.ts +122 -0
  10. package/src/commands/index.ts +25 -0
  11. package/src/commands/install.ts +19 -0
  12. package/src/commands/options.ts +308 -0
  13. package/src/commands/recorder.ts +41 -0
  14. package/src/commands/run.ts +82 -0
  15. package/src/commands/runOnly.ts +56 -0
  16. package/src/dependencies/coursier.ts +84 -0
  17. package/src/dependencies/download.ts +11 -0
  18. package/src/dependencies/graalVm.ts +74 -0
  19. package/src/dependencies/index.ts +45 -0
  20. package/src/dependencies/os.ts +24 -0
  21. package/src/dependencies/versions.ts +15 -0
  22. package/src/enterprise.ts +227 -0
  23. package/src/index.ts +5 -0
  24. package/src/java.ts +48 -0
  25. package/src/log.ts +19 -0
  26. package/src/readline.ts +39 -0
  27. package/src/run.ts +67 -0
  28. package/src/simulations.ts +29 -0
  29. package/target/{bundle.d.ts → bundle/index.d.ts} +2 -1
  30. package/target/{bundle.js → bundle/index.js} +7 -1
  31. package/target/bundle/polyfill.d.ts +2 -0
  32. package/target/bundle/polyfill.js +83 -0
  33. package/target/commands/build.d.ts +3 -0
  34. package/target/commands/build.js +22 -0
  35. package/target/commands/enterpriseDeploy.d.ts +3 -0
  36. package/target/commands/enterpriseDeploy.js +64 -0
  37. package/target/commands/enterprisePackage.d.ts +3 -0
  38. package/target/commands/enterprisePackage.js +28 -0
  39. package/target/commands/enterpriseStart.d.ts +3 -0
  40. package/target/commands/enterpriseStart.js +80 -0
  41. package/target/commands/index.d.ts +2 -0
  42. package/target/commands/index.js +28 -0
  43. package/target/commands/install.d.ts +3 -0
  44. package/target/commands/install.js +18 -0
  45. package/target/commands/options.d.ts +50 -0
  46. package/target/commands/options.js +214 -0
  47. package/target/commands/recorder.d.ts +3 -0
  48. package/target/commands/recorder.js +28 -0
  49. package/target/commands/run.d.ts +3 -0
  50. package/target/commands/run.js +53 -0
  51. package/target/commands/runOnly.d.ts +3 -0
  52. package/target/commands/runOnly.js +37 -0
  53. package/target/dependencies/coursier.d.ts +1 -1
  54. package/target/dependencies/coursier.js +11 -6
  55. package/target/dependencies/index.d.ts +2 -0
  56. package/target/dependencies/index.js +4 -2
  57. package/target/dependencies/versions.d.ts +3 -0
  58. package/target/dependencies/versions.js +9 -6
  59. package/target/enterprise.d.ts +2 -1
  60. package/target/enterprise.js +7 -6
  61. package/target/index.js +2 -337
  62. package/target/run.js +4 -2
  63. package/tsconfig.json +18 -0
@@ -0,0 +1,308 @@
1
+ import { Option, Argument } from "commander";
2
+ import fs from "fs";
3
+ import os from "os";
4
+ import path from "path";
5
+
6
+ import { SimulationFile } from "../simulations";
7
+ import { keyInSelectPaginated } from "../readline";
8
+ import { logger } from "../log";
9
+
10
+ const getStringValueOptional =
11
+ (option: Option) =>
12
+ (options: any): string | undefined => {
13
+ const value = options[option.attributeName()];
14
+ if (typeof value === "string" || typeof value === "undefined") {
15
+ return value;
16
+ } else {
17
+ throw Error(
18
+ `Expected type string|undefined for attribute ${option.attributeName()}, got ${typeof value} - please report this as a bug.`
19
+ );
20
+ }
21
+ };
22
+ const getStringValueMandatory =
23
+ (option: Option) =>
24
+ (options: any): string => {
25
+ const value = options[option.attributeName()];
26
+ if (typeof value === "string") {
27
+ return value;
28
+ } else {
29
+ throw Error(
30
+ `Expected type string for attribute ${option.attributeName()}, got ${typeof value} - please report this as a bug.`
31
+ );
32
+ }
33
+ };
34
+ const getBooleanValueOptional =
35
+ (option: Option) =>
36
+ (options: any): boolean | undefined => {
37
+ const value = options[option.attributeName()];
38
+ if (typeof value === "boolean" || typeof value === "undefined") {
39
+ return value;
40
+ } else {
41
+ throw Error(
42
+ `Expected type boolean|undefined for attribute ${option.attributeName()}, got ${typeof value} - please report this as a bug.`
43
+ );
44
+ }
45
+ };
46
+ const getBooleanValueMandatory =
47
+ (option: Option) =>
48
+ (options: any): boolean => {
49
+ const value = options[option.attributeName()];
50
+ if (typeof value === "boolean") {
51
+ return value;
52
+ } else {
53
+ throw Error(
54
+ `Expected type boolean for attribute ${option.attributeName()}, got ${typeof value} - please report this as a bug.`
55
+ );
56
+ }
57
+ };
58
+ const getNumberValueOptional =
59
+ (option: Option) =>
60
+ (options: any): number | undefined => {
61
+ const value = options[option.attributeName()];
62
+ if (typeof value === "number" || typeof value === "undefined") {
63
+ return value;
64
+ } else {
65
+ throw Error(
66
+ `Expected type number|undefined for attribute ${option.attributeName()}, got ${typeof value} - please report this as a bug.`
67
+ );
68
+ }
69
+ };
70
+
71
+ export const gatlingHomeOption = new Option(
72
+ "--gatling-home <value>",
73
+ 'The folder used to download and install Gatling components (default: "~/.gatling")'
74
+ );
75
+ export const gatlingHomeOptionValueWithDefaults = (options: any): string =>
76
+ getStringValueOptional(gatlingHomeOption)(options) || `${os.homedir()}/.gatling`;
77
+
78
+ export const sourcesFolderOption = new Option("--sources-folder <value>", "The sources folder path").default("src");
79
+ export const sourcesFolderOptionValue = getStringValueMandatory(sourcesFolderOption);
80
+
81
+ export const simulationOption = new Option(
82
+ "--simulation <value>",
83
+ "The simulation entry point function name (default: if only one *.gatling.js or *.gatling.ts file is found, will execute that simulation)"
84
+ );
85
+ export const simulationOptionValueWithDefaults = (
86
+ options: any,
87
+ simulationsFound: SimulationFile[],
88
+ interactive: boolean
89
+ ): string => {
90
+ const declaredSimulation = getStringValueOptional(simulationOption)(options);
91
+ if (declaredSimulation !== undefined) {
92
+ return declaredSimulation;
93
+ } else if (simulationsFound.length === 1) {
94
+ return simulationsFound[0].name;
95
+ } else if (simulationsFound.length === 0) {
96
+ throw new Error(
97
+ "No simulation found, simulations must be defined in a <simulation name>.gatling.js or <simulation name>.gatling.ts file)"
98
+ );
99
+ } else if (interactive) {
100
+ const sortedSimulations = simulationsFound.sort((a, b) => a.name.localeCompare(b.name));
101
+ const idx = keyInSelectPaginated(
102
+ sortedSimulations.map((s) => s.name),
103
+ "Choose a simulation to run"
104
+ );
105
+ if (idx >= 0) {
106
+ const simulation = sortedSimulations[idx].name;
107
+ logger.info(`Simulation '${simulation}' was chosen.`);
108
+ return simulation;
109
+ } else {
110
+ throw new Error("Simulation choice was cancelled.");
111
+ }
112
+ } else {
113
+ throw new Error(
114
+ `Several simulations found, specify one using the --simulation option (available simulations: ${simulationsFound.map((s) => s.name)})`
115
+ );
116
+ }
117
+ };
118
+
119
+ export const simulationMandatoryOption = new Option(
120
+ "--simulation <value>",
121
+ "The simulation entry point function name"
122
+ ).makeOptionMandatory(true);
123
+ export const simulationMandatoryOptionValue = getStringValueMandatory(simulationMandatoryOption);
124
+
125
+ export const bundleFileOption = new Option(
126
+ "--bundle-file <value>",
127
+ "The target bundle file path when building simulations (must have a .js extension)"
128
+ )
129
+ .default("target/bundle.js")
130
+ .argParser((value) => {
131
+ if (!value.endsWith(".js")) {
132
+ throw Error(`'${value}' is not a valid bundle file path: should have a .js extension`);
133
+ }
134
+ return value;
135
+ });
136
+ export const bundleFileOptionValue = getStringValueMandatory(bundleFileOption);
137
+
138
+ export const packageFileOption = new Option(
139
+ "--package-file <value>",
140
+ "The target package file path when packaging simulations for Gatling Enterprise (must have a .zip extension)"
141
+ )
142
+ .default("target/package.zip")
143
+ .argParser((value) => {
144
+ if (!value.endsWith(".zip")) {
145
+ throw Error(`'${value}' is not a valid package file path: should have a .zip extension`);
146
+ }
147
+ return value;
148
+ });
149
+ export const packageFileOptionValue = getStringValueMandatory(packageFileOption);
150
+
151
+ export const resourcesFolderOption = new Option("--resources-folder <value>", "The resources folder path").default(
152
+ "resources"
153
+ );
154
+ export const resourcesFolderOptionValue = getStringValueMandatory(resourcesFolderOption);
155
+
156
+ export const resultsFolderOption = new Option("--results-folder <value>", "The results folder path").default(
157
+ "target/gatling"
158
+ );
159
+ export const resultsFolderOptionValue = getStringValueMandatory(resultsFolderOption);
160
+
161
+ export const typescriptOption = new Option(
162
+ "--typescript",
163
+ "Use the typescript compiler to compile your code (default: true if the sourcesFolder contains any *.gatling.ts file, false otherwise)"
164
+ );
165
+ export const typescriptOptionValueWithDefaults = (options: any, simulationsFound: SimulationFile[]): boolean => {
166
+ const value = getBooleanValueOptional(typescriptOption)(options);
167
+ return value !== undefined ? value : simulationsFound.findIndex((s) => s.type === "typescript") >= 0;
168
+ };
169
+
170
+ export const graalvmHomeMandatoryOption = new Option(
171
+ "--graalvm-home <value>",
172
+ "Path to the GraalVM home"
173
+ ).makeOptionMandatory(true);
174
+ export const graalvmHomeMandatoryOptionValue = getStringValueMandatory(graalvmHomeMandatoryOption);
175
+
176
+ export const jvmClasspathMandatoryOption = new Option(
177
+ "--jvm-classpath <value>",
178
+ "The classpath containing all Gatling JVM components"
179
+ ).makeOptionMandatory(true);
180
+ export const jvmClasspathMandatoryOptionValue = getStringValueMandatory(jvmClasspathMandatoryOption);
181
+
182
+ export const memoryOption = new Option(
183
+ "--memory <value>",
184
+ "Heap space memory size in MiB for Gatling. Half the total available memory is usually a good default, as the Gatling process will use more memory than just the heap space."
185
+ ).argParser((value) => {
186
+ const parsedValue = parseInt(value, 10);
187
+ if (isNaN(parsedValue)) {
188
+ throw new Error(`${value} is not a valid memory size, must be an integer number`);
189
+ }
190
+ return parsedValue;
191
+ });
192
+ export const memoryOptionValue = getNumberValueOptional(memoryOption);
193
+
194
+ export const nonInteractiveOption = new Option(
195
+ "--non-interactive",
196
+ "Switch to non-interactive mode and fail if no simulation is explicitly specified"
197
+ ).default(false);
198
+ export const nonInteractiveOptionValue = getBooleanValueMandatory(nonInteractiveOption);
199
+
200
+ export const postmanOption = new Option(
201
+ "--postman <version>",
202
+ "Postman compatibility option: adds polyfills, etc."
203
+ ).hideHelp();
204
+ export const postmanOptionValueWithDefaults = (options: any): string | undefined => {
205
+ const postmanOptionValue = getStringValueOptional(postmanOption)(options);
206
+ if (postmanOptionValue !== undefined) {
207
+ return postmanOptionValue;
208
+ } else {
209
+ try {
210
+ const conf = JSON.parse(fs.readFileSync("package.json", { encoding: "utf-8", flag: "r" }));
211
+ const withPostman =
212
+ conf.dependencies?.["@gatling.io/postman"] !== undefined ||
213
+ conf.devDependencies?.["@gatling.io/postman"] !== undefined;
214
+ if (withPostman) {
215
+ let directory = path.normalize(path.dirname("package.json"));
216
+ const root = path.parse(directory).root;
217
+ while (true) {
218
+ const file = path.join(directory, "node_modules", "@gatling.io", "postman", "package.json");
219
+ if (fs.existsSync(file)) {
220
+ const installedPackage = JSON.parse(fs.readFileSync(file, { encoding: "utf-8", flag: "r" }));
221
+ return installedPackage.version;
222
+ } else if (directory === root) {
223
+ return undefined;
224
+ } else {
225
+ directory = path.normalize(path.join(directory, ".."));
226
+ }
227
+ }
228
+ } else {
229
+ return undefined;
230
+ }
231
+ } catch {
232
+ return undefined;
233
+ }
234
+ }
235
+ };
236
+
237
+ export const runParametersArgument = new Argument(
238
+ "[optionKey=optionValue...]",
239
+ "Specify one or more parameter which can be read in the simulation script with the getParameter() function; format must be key=value"
240
+ );
241
+ export const parseRunParametersArgument = (args: string[]): Record<string, string> => {
242
+ const parsedParameters: Record<string, string> = {};
243
+ for (const arg of args) {
244
+ const i = arg.indexOf("=");
245
+ if (i < 0) {
246
+ throw Error(`Parameter '${arg}' is not valid: format should be key=value`);
247
+ } else {
248
+ const key = arg.slice(0, i).trim();
249
+ parsedParameters[key] = arg.slice(i + 1);
250
+ }
251
+ }
252
+ return parsedParameters;
253
+ };
254
+
255
+ export const apiUrlOption = new Option("--apiUrl <value>", "URL of the Gatling Enterprise API")
256
+ .default("https://api.gatling.io")
257
+ .hideHelp();
258
+ export const apiUrlOptionValue = getStringValueMandatory(apiUrlOption);
259
+
260
+ export const webAppUrlOption = new Option("--webAppUrl <value>", "URL of the Gatling Enterprise web app")
261
+ .default("https://cloud.gatling.io")
262
+ .hideHelp();
263
+ export const webAppUrlOptionValue = getStringValueMandatory(webAppUrlOption);
264
+
265
+ export const apiTokenOption = new Option(
266
+ "--api-token <value>",
267
+ "API Token on Gatling Enterprise. Prefer configuration using `GATLING_ENTERPRISE_API_TOKEN` environment variable."
268
+ );
269
+ export const apiTokenOptionValue = getStringValueOptional(apiTokenOption);
270
+
271
+ // Plugin configuration
272
+
273
+ export const controlPlaneUrlOption = new Option(
274
+ "--control-plane-url <value>",
275
+ "URL of a control plane for Gatling Enterprise providing a private repository. If this parameter is provided, packages will be registered as private packages and uploaded through this private control plane."
276
+ );
277
+ export const controlPlaneUrlOptionValue = getStringValueOptional(controlPlaneUrlOption);
278
+
279
+ // Descriptor file
280
+
281
+ export const packageDescriptorFilenameOption = new Option(
282
+ "--package-descriptor-filename <value>",
283
+ "Path to a package descriptor inside the .gatling folder"
284
+ ).default("package.conf");
285
+ export const packageDescriptorFilenameOptionValue = getStringValueMandatory(packageDescriptorFilenameOption);
286
+
287
+ // Deployment info
288
+
289
+ export const enterpriseSimulationOption = new Option(
290
+ "--enterprise-simulation <value>",
291
+ "Specify the simulation name directly to bypass the prompt using the following command."
292
+ );
293
+ export const enterpriseSimulationOptionValue = getStringValueOptional(enterpriseSimulationOption);
294
+
295
+ export const runTitleOption = new Option("--run-title <value>", "Allows setting a title for your run reports.");
296
+ export const runTitleOptionValue = getStringValueOptional(runTitleOption);
297
+
298
+ export const runDescriptionOption = new Option(
299
+ "--run-description <value>",
300
+ "Allows setting a description for your run reports summary."
301
+ );
302
+ export const runDescriptionOptionValue = getStringValueOptional(runDescriptionOption);
303
+
304
+ export const waitForRunEndOption = new Option(
305
+ "--wait-for-run-end",
306
+ "Wait for the result after starting the simulation on Gatling Enterprise, and complete with an error if the simulation ends with any error status"
307
+ ).default(false);
308
+ export const waitForRunEndOptionValue = getBooleanValueMandatory(waitForRunEndOption);
@@ -0,0 +1,41 @@
1
+ import { Command } from "commander";
2
+
3
+ import {
4
+ gatlingHomeOption,
5
+ gatlingHomeOptionValueWithDefaults,
6
+ resourcesFolderOption,
7
+ resourcesFolderOptionValue,
8
+ sourcesFolderOption,
9
+ sourcesFolderOptionValue,
10
+ typescriptOption,
11
+ typescriptOptionValueWithDefaults
12
+ } from "./options";
13
+ import { findSimulations } from "../simulations";
14
+ import { installRecorder } from "../dependencies";
15
+ import { logger } from "../log";
16
+ import { runRecorder } from "../run";
17
+
18
+ export default (program: Command): void => {
19
+ program
20
+ .command("recorder")
21
+ .description("Run the Gatling recorder")
22
+ .addOption(gatlingHomeOption)
23
+ .addOption(sourcesFolderOption)
24
+ .addOption(typescriptOption)
25
+ .addOption(resourcesFolderOption)
26
+ .action(async (options) => {
27
+ const gatlingHome: string = gatlingHomeOptionValueWithDefaults(options);
28
+ const sourcesFolder: string = sourcesFolderOptionValue(options);
29
+ const resourcesFolder: string = resourcesFolderOptionValue(options);
30
+
31
+ const simulations = await findSimulations(sourcesFolder);
32
+ const typescript = typescriptOptionValueWithDefaults(options, simulations);
33
+
34
+ const { graalvmHome, coursierBinary, jvmClasspath } = await installRecorder({ gatlingHome });
35
+ logger.debug(`graalvmHome=${graalvmHome}`);
36
+ logger.debug(`coursierBinary=${coursierBinary}`);
37
+ logger.debug(`jvmClasspath=${jvmClasspath}`);
38
+
39
+ await runRecorder({ graalvmHome, jvmClasspath, sourcesFolder, typescript, resourcesFolder });
40
+ });
41
+ };
@@ -0,0 +1,82 @@
1
+ import { Command } from "commander";
2
+ import {
3
+ bundleFileOption,
4
+ bundleFileOptionValue,
5
+ gatlingHomeOption,
6
+ gatlingHomeOptionValueWithDefaults,
7
+ memoryOption,
8
+ memoryOptionValue,
9
+ nonInteractiveOption,
10
+ nonInteractiveOptionValue,
11
+ parseRunParametersArgument,
12
+ postmanOption,
13
+ postmanOptionValueWithDefaults,
14
+ resourcesFolderOption,
15
+ resourcesFolderOptionValue,
16
+ resultsFolderOption,
17
+ resultsFolderOptionValue,
18
+ runParametersArgument,
19
+ simulationOption,
20
+ simulationOptionValueWithDefaults,
21
+ sourcesFolderOption,
22
+ sourcesFolderOptionValue,
23
+ typescriptOption,
24
+ typescriptOptionValueWithDefaults
25
+ } from "./options";
26
+ import { findSimulations } from "../simulations";
27
+ import { installGatlingJs } from "../dependencies";
28
+ import { logger } from "../log";
29
+ import { bundle } from "../bundle";
30
+ import { runSimulation } from "../run";
31
+
32
+ export default (program: Command): void => {
33
+ program
34
+ .command("run")
35
+ .description(
36
+ "Build and run a Gatling simulation, after installing all required components and dependencies for Gatling"
37
+ )
38
+ .addOption(sourcesFolderOption)
39
+ .addOption(simulationOption)
40
+ .addOption(typescriptOption)
41
+ .addOption(bundleFileOption)
42
+ .addOption(resourcesFolderOption)
43
+ .addOption(resultsFolderOption)
44
+ .addOption(gatlingHomeOption)
45
+ .addOption(memoryOption)
46
+ .addOption(postmanOption)
47
+ .addOption(nonInteractiveOption)
48
+ .addArgument(runParametersArgument)
49
+ .action(async (args: string[], options) => {
50
+ const gatlingHome = gatlingHomeOptionValueWithDefaults(options);
51
+ const sourcesFolder: string = sourcesFolderOptionValue(options);
52
+ const bundleFile = bundleFileOptionValue(options);
53
+ const resourcesFolder: string = resourcesFolderOptionValue(options);
54
+ const resultsFolder: string = resultsFolderOptionValue(options);
55
+ const memory: number | undefined = memoryOptionValue(options);
56
+ const nonInteractive: boolean = nonInteractiveOptionValue(options);
57
+ const postman = postmanOptionValueWithDefaults(options);
58
+ const runParameters = parseRunParametersArgument(args);
59
+
60
+ const simulations = await findSimulations(sourcesFolder);
61
+ const typescript = typescriptOptionValueWithDefaults(options, simulations);
62
+ const simulation = simulationOptionValueWithDefaults(options, simulations, !nonInteractive);
63
+
64
+ const { graalvmHome, coursierBinary, jvmClasspath } = await installGatlingJs({ gatlingHome, postman });
65
+ logger.debug(`graalvmHome=${graalvmHome}`);
66
+ logger.debug(`coursierBinary=${coursierBinary}`);
67
+ logger.debug(`jvmClasspath=${jvmClasspath}`);
68
+
69
+ await bundle({ sourcesFolder, bundleFile, postman, typescript, simulations });
70
+
71
+ await runSimulation({
72
+ graalvmHome,
73
+ jvmClasspath,
74
+ simulation,
75
+ bundleFile,
76
+ resourcesFolder,
77
+ resultsFolder,
78
+ memory,
79
+ runParameters
80
+ });
81
+ });
82
+ };
@@ -0,0 +1,56 @@
1
+ import { Command } from "commander";
2
+
3
+ import {
4
+ bundleFileOption,
5
+ bundleFileOptionValue,
6
+ graalvmHomeMandatoryOption,
7
+ graalvmHomeMandatoryOptionValue,
8
+ jvmClasspathMandatoryOption,
9
+ jvmClasspathMandatoryOptionValue,
10
+ memoryOption,
11
+ memoryOptionValue,
12
+ parseRunParametersArgument,
13
+ resourcesFolderOption,
14
+ resourcesFolderOptionValue,
15
+ resultsFolderOption,
16
+ resultsFolderOptionValue,
17
+ runParametersArgument,
18
+ simulationMandatoryOption,
19
+ simulationMandatoryOptionValue
20
+ } from "./options";
21
+ import { runSimulation } from "../run";
22
+
23
+ export default (program: Command): void => {
24
+ program
25
+ .command("run-only")
26
+ .description("Run a Gatling simulation from an already built bundle")
27
+ .addOption(graalvmHomeMandatoryOption)
28
+ .addOption(jvmClasspathMandatoryOption)
29
+ .addOption(simulationMandatoryOption)
30
+ .addOption(bundleFileOption)
31
+ .addOption(resourcesFolderOption)
32
+ .addOption(resultsFolderOption)
33
+ .addOption(memoryOption)
34
+ .addArgument(runParametersArgument)
35
+ .action(async (args: string[], options) => {
36
+ const graalvmHome: string = graalvmHomeMandatoryOptionValue(options);
37
+ const jvmClasspath: string = jvmClasspathMandatoryOptionValue(options);
38
+ const simulation: string = simulationMandatoryOptionValue(options);
39
+ const bundleFile = bundleFileOptionValue(options);
40
+ const resourcesFolder: string = resourcesFolderOptionValue(options);
41
+ const resultsFolder: string = resultsFolderOptionValue(options);
42
+ const memory: number | undefined = memoryOptionValue(options);
43
+ const runParameters = parseRunParametersArgument(args);
44
+
45
+ await runSimulation({
46
+ graalvmHome,
47
+ jvmClasspath,
48
+ simulation: simulation,
49
+ bundleFile,
50
+ resourcesFolder,
51
+ resultsFolder,
52
+ memory,
53
+ runParameters
54
+ });
55
+ });
56
+ };
@@ -0,0 +1,84 @@
1
+ import { existsSync } from "fs";
2
+ import fs from "fs/promises";
3
+
4
+ import { downloadFile } from "./download";
5
+ import { logger } from "../log";
6
+ import { versions } from "./versions";
7
+ import { promisify } from "util";
8
+ import { exec } from "child_process";
9
+ import { osType } from "./os";
10
+
11
+ export const installCoursier = async (gatlingHomeDir: string, downloadDir: string): Promise<string> => {
12
+ const coursierRootPath = `${gatlingHomeDir}/coursier/${versions.coursier}`;
13
+ const coursierPath = `${coursierRootPath}/cs`;
14
+
15
+ if (!existsSync(coursierPath)) {
16
+ const jarUrl = `https://github.com/coursier/coursier/releases/download/v${versions.coursier}/coursier`;
17
+ const windowsBatUrl = `https://github.com/coursier/launchers/raw/master/coursier.bat`;
18
+ const downloadPath = `${downloadDir}/cs`;
19
+
20
+ if (existsSync(coursierRootPath)) {
21
+ await fs.rm(coursierRootPath, { recursive: true });
22
+ }
23
+ if (existsSync(downloadPath)) {
24
+ await fs.rm(downloadPath);
25
+ }
26
+ await fs.mkdir(coursierRootPath, { recursive: true });
27
+
28
+ logger.info(`Downloading Coursier ${versions.coursier} to ${downloadPath}`);
29
+ await downloadFile(jarUrl, downloadPath);
30
+ if (osType === "Windows_NT") {
31
+ await downloadFile(windowsBatUrl, `${downloadPath}.bat`);
32
+ } else {
33
+ await fs.chmod(downloadPath, 0o744);
34
+ }
35
+
36
+ logger.info(`Installing Coursier to ${coursierPath}`);
37
+ await fs.rename(downloadPath, coursierPath);
38
+ if (osType === "Windows_NT") {
39
+ await fs.rename(`${downloadPath}.bat`, `${coursierPath}.bat`);
40
+ }
41
+ } else {
42
+ logger.info(`Coursier ${versions.coursier} already installed at ${coursierPath}`);
43
+ }
44
+
45
+ return coursierPath;
46
+ };
47
+
48
+ export const resolveGatlingJsDependencies = async (
49
+ coursierPath: string,
50
+ javaHome: string,
51
+ postmanVersion?: string
52
+ ): Promise<string> => {
53
+ const dependencies = [
54
+ `"io.gatling.highcharts:gatling-charts-highcharts:${versions.gatling.core}"`,
55
+ `"io.gatling:gatling-jvm-to-js-adapter:${versions.gatling.jsAdapter}"`,
56
+ `"io.gatling:gatling-enterprise-plugin-commons:${versions.gatling.enterprisePluginCommons}"`,
57
+ `"org.graalvm.polyglot:js-community:${versions.graalvm.js}"`
58
+ ];
59
+ if (postmanVersion !== undefined) {
60
+ dependencies.push(`"io.gatling:gatling-postman-jvm-to-js-adapter:${postmanVersion}"`);
61
+ }
62
+
63
+ return await resolveDependencies(coursierPath, javaHome, ...dependencies);
64
+ };
65
+
66
+ export const resolveRecorderDependencies = async (coursierPath: string, javaHome: string): Promise<string> => {
67
+ const recorderDep = `io.gatling:gatling-recorder:${versions.gatling.core}`;
68
+ return await resolveDependencies(coursierPath, javaHome, recorderDep);
69
+ };
70
+
71
+ const resolveDependencies = async (
72
+ coursierPath: string,
73
+ javaHome: string,
74
+ ...dependencies: string[]
75
+ ): Promise<string> => {
76
+ const command = `"${coursierPath}" fetch --classpath ${dependencies.join(" ")}`;
77
+
78
+ // TODO could add a timeout
79
+ logger.info(`Resolving dependencies with Coursier`);
80
+ const { stdout } = await execAsync(command, { env: { ...process.env, JAVA_HOME: javaHome } });
81
+ return stdout;
82
+ };
83
+
84
+ const execAsync = promisify(exec);
@@ -0,0 +1,11 @@
1
+ import axios from "axios";
2
+ import fs from "fs";
3
+ import stream from "stream";
4
+ import util from "util";
5
+
6
+ const pipeline = util.promisify(stream.pipeline);
7
+
8
+ export const downloadFile = async (url: string, targetFile: string): Promise<void> => {
9
+ const request = await axios.get(url, { responseType: "stream" });
10
+ await pipeline(request.data, fs.createWriteStream(targetFile));
11
+ };
@@ -0,0 +1,74 @@
1
+ import { existsSync } from "fs";
2
+ import fs from "fs/promises";
3
+ import decompress from "decompress";
4
+
5
+ import { downloadFile } from "./download";
6
+ import { logger } from "../log";
7
+ import { osType, osArch } from "./os";
8
+ import { versions } from "./versions";
9
+
10
+ export const installGraalVm = async (gatlingHomeDir: string, downloadDir: string): Promise<string> => {
11
+ const { os, arch, extension, homePath, binPath } = graalVmPlatformParams();
12
+ const graalvmRootPath = `${gatlingHomeDir}/graalvm/${versions.graalvm.jdk}`;
13
+ const graalvmHomePath = `${graalvmRootPath}${homePath}`;
14
+ const graalvmJavaPath = `${graalvmHomePath}${binPath}`;
15
+
16
+ if (!existsSync(graalvmJavaPath)) {
17
+ const url = `https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-${versions.graalvm.jdk}/graalvm-community-jdk-${versions.graalvm.jdk}_${os}-${arch}_bin.${extension}`;
18
+ const downloadPath = `${downloadDir}/graalvm.${extension}`;
19
+
20
+ if (existsSync(graalvmRootPath)) {
21
+ await fs.rm(graalvmRootPath, { recursive: true });
22
+ }
23
+ if (existsSync(downloadPath)) {
24
+ await fs.rm(downloadPath);
25
+ }
26
+ await fs.mkdir(graalvmRootPath, { recursive: true });
27
+
28
+ logger.info(`Downloading GraalVM Community Edition ${versions.graalvm.jdk} to ${downloadPath}`);
29
+ await downloadFile(url, downloadPath);
30
+
31
+ logger.info(`Unpacking GraalVM to ${graalvmRootPath}`);
32
+ await decompress(downloadPath, graalvmRootPath, {
33
+ map: (file) => {
34
+ // Remove first level of file name, because it already contains a root directory
35
+ file.path = file.path.split("/").slice(1).join("/");
36
+ return file;
37
+ }
38
+ });
39
+
40
+ await fs.rm(downloadPath);
41
+ } else {
42
+ logger.info(`GraalVM Community Edition ${versions.graalvm.jdk} already installed at ${graalvmRootPath}`);
43
+ }
44
+
45
+ return graalvmHomePath;
46
+ };
47
+
48
+ const graalVmPlatformParams = () => {
49
+ if (osType === "Linux") {
50
+ const os = "linux";
51
+ const extension = "tar.gz";
52
+ const homePath = "";
53
+ const binPath = "/bin/java";
54
+ if (osArch === "x64") {
55
+ return { os, arch: "x64", extension, homePath, binPath };
56
+ } else if (osArch === "arm64") {
57
+ return { os, arch: "aarch64", extension, homePath, binPath };
58
+ }
59
+ } else if (osType === "Darwin") {
60
+ const os = "macos";
61
+ const extension = "tar.gz";
62
+ const homePath = "/Contents/Home";
63
+ const binPath = "/bin/java";
64
+ if (osArch === "x64") {
65
+ return { os, arch: "x64", extension, homePath, binPath };
66
+ } else if (osArch === "arm64") {
67
+ return { os, arch: "aarch64", extension, homePath, binPath };
68
+ }
69
+ } else if (osType === "Windows_NT" && osArch === "x64") {
70
+ return { os: "windows", arch: "x64", extension: "zip", homePath: "", binPath: "/bin/java.exe" };
71
+ }
72
+
73
+ throw Error(`Operating system type '${osType}' with architecture '${osArch}' is not currently supported.`);
74
+ };