@elastic/synthetics 1.17.2 → 1.18.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/dist/cli.js.map +1 -1
- package/dist/common_types.d.ts.map +1 -1
- package/dist/core/globals.d.ts +1 -0
- package/dist/core/globals.d.ts.map +1 -1
- package/dist/core/globals.js +12 -1
- package/dist/core/globals.js.map +1 -1
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +2 -7
- package/dist/core/logger.js.map +1 -1
- package/dist/core/mfa.d.ts.map +1 -1
- package/dist/core/mfa.js +1 -1
- package/dist/dsl/monitor.d.ts +4 -0
- package/dist/dsl/monitor.d.ts.map +1 -1
- package/dist/dsl/monitor.js +6 -0
- package/dist/dsl/monitor.js.map +1 -1
- package/dist/formatter/javascript.d.ts.map +1 -1
- package/dist/formatter/javascript.js.map +1 -1
- package/dist/generator/utils.d.ts +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js.map +1 -1
- package/dist/plugins/tracing.js.map +1 -1
- package/dist/push/bundler.d.ts +2 -5
- package/dist/push/bundler.d.ts.map +1 -1
- package/dist/push/bundler.js +15 -30
- package/dist/push/bundler.js.map +1 -1
- package/dist/push/index.d.ts.map +1 -1
- package/dist/push/index.js +21 -6
- package/dist/push/index.js.map +1 -1
- package/dist/push/monitor.d.ts +4 -1
- package/dist/push/monitor.d.ts.map +1 -1
- package/dist/push/monitor.js +13 -1
- package/dist/push/monitor.js.map +1 -1
- package/dist/push/run-local.d.ts +27 -0
- package/dist/push/run-local.d.ts.map +1 -0
- package/dist/push/run-local.js +132 -0
- package/dist/push/run-local.js.map +1 -0
- package/dist/push/utils.d.ts +2 -0
- package/dist/push/utils.d.ts.map +1 -1
- package/dist/push/utils.js +33 -1
- package/dist/push/utils.js.map +1 -1
- package/dist/reporters/base.d.ts.map +1 -1
- package/dist/reporters/base.js.map +1 -1
- package/dist/reporters/build_kite_cli.d.ts.map +1 -1
- package/dist/reporters/build_kite_cli.js.map +1 -1
- package/dist/reporters/json.d.ts +1 -1
- package/dist/reporters/json.d.ts.map +1 -1
- package/dist/reporters/json.js +6 -2
- package/dist/reporters/json.js.map +1 -1
- package/dist/reporters/junit.d.ts.map +1 -1
- package/dist/reporters/junit.js.map +1 -1
- package/package.json +8 -5
- package/src/cli.ts +8 -3
- package/src/common_types.ts +1 -1
- package/src/core/globals.ts +12 -0
- package/src/core/logger.ts +2 -8
- package/src/core/mfa.ts +11 -11
- package/src/dsl/monitor.ts +7 -0
- package/src/formatter/javascript.ts +3 -1
- package/src/helpers.ts +3 -3
- package/src/loader.ts +1 -1
- package/src/plugins/tracing.ts +1 -1
- package/src/push/bundler.ts +16 -36
- package/src/push/index.ts +28 -10
- package/src/push/monitor.ts +16 -4
- package/src/push/run-local.ts +155 -0
- package/src/push/utils.ts +51 -5
- package/src/reporters/base.ts +8 -4
- package/src/reporters/build_kite_cli.ts +7 -6
- package/src/reporters/json.ts +10 -14
- package/src/reporters/junit.ts +4 -10
package/src/cli.ts
CHANGED
|
@@ -278,12 +278,17 @@ program
|
|
|
278
278
|
// TOTP command
|
|
279
279
|
program
|
|
280
280
|
.command('totp <secret>')
|
|
281
|
-
.description(
|
|
282
|
-
|
|
281
|
+
.description(
|
|
282
|
+
'Generate a Time-based One-Time token using the provided secret.'
|
|
283
|
+
)
|
|
284
|
+
.option(
|
|
285
|
+
'--issuer <issuer>',
|
|
286
|
+
'Provider or Service the secret is associated with.'
|
|
287
|
+
)
|
|
283
288
|
.option('--label <label>', 'Account Identifier (default: SyntheticsTOTP)')
|
|
284
289
|
.action((secret, cmdOpts: TOTPCmdOptions) => {
|
|
285
290
|
try {
|
|
286
|
-
const token = totp(secret, cmdOpts)
|
|
291
|
+
const token = totp(secret, cmdOpts);
|
|
287
292
|
write(bold(`OTP Token: ${token}`));
|
|
288
293
|
} catch (e) {
|
|
289
294
|
error(e);
|
package/src/common_types.ts
CHANGED
package/src/core/globals.ts
CHANGED
|
@@ -34,4 +34,16 @@ if (!global[SYNTHETICS_RUNNER]) {
|
|
|
34
34
|
global[SYNTHETICS_RUNNER] = new Runner();
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Set debug based on DEBUG ENV and namespace - synthetics
|
|
39
|
+
*/
|
|
40
|
+
if (process.env.DEBUG && process.env.DEBUG.includes('synthetics')) {
|
|
41
|
+
process.env['__SYNTHETICS__DEBUG__'] = '1';
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
export const runner: Runner = global[SYNTHETICS_RUNNER];
|
|
45
|
+
|
|
46
|
+
// is Debug mode enabled
|
|
47
|
+
export function inDebugMode() {
|
|
48
|
+
return !!process.env['__SYNTHETICS__DEBUG__'];
|
|
49
|
+
}
|
package/src/core/logger.ts
CHANGED
|
@@ -25,16 +25,10 @@
|
|
|
25
25
|
|
|
26
26
|
import { grey, cyan, dim, italic } from 'kleur/colors';
|
|
27
27
|
import { now } from '../helpers';
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Set debug based on DEBUG ENV and namespace - synthetics
|
|
31
|
-
*/
|
|
32
|
-
if (process.env.DEBUG && process.env.DEBUG.includes('synthetics')) {
|
|
33
|
-
process.env['__SYNTHETICS__DEBUG__'] = '1';
|
|
34
|
-
}
|
|
28
|
+
import { inDebugMode } from './globals';
|
|
35
29
|
|
|
36
30
|
export function log(msg) {
|
|
37
|
-
if (!
|
|
31
|
+
if (!inDebugMode() || !msg) {
|
|
38
32
|
return;
|
|
39
33
|
}
|
|
40
34
|
if (typeof msg === 'object') {
|
package/src/core/mfa.ts
CHANGED
|
@@ -23,48 +23,48 @@
|
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { TOTP } from
|
|
26
|
+
import { TOTP } from 'otpauth';
|
|
27
27
|
|
|
28
28
|
type TOTPOptions = {
|
|
29
29
|
/**
|
|
30
30
|
* Provider or Service the secret is associated with
|
|
31
31
|
*/
|
|
32
|
-
issuer?: string
|
|
32
|
+
issuer?: string;
|
|
33
33
|
/**
|
|
34
34
|
* Account Identifier.
|
|
35
35
|
* @default 'SyntheticsTOTP'
|
|
36
36
|
*/
|
|
37
|
-
label?: string
|
|
37
|
+
label?: string;
|
|
38
38
|
/**
|
|
39
39
|
* Include issuer prefix in label.
|
|
40
40
|
*/
|
|
41
|
-
issuerInLabel?: boolean
|
|
41
|
+
issuerInLabel?: boolean;
|
|
42
42
|
/**
|
|
43
43
|
* The encoded secret key used to generate the TOTP.
|
|
44
44
|
*/
|
|
45
|
-
secret?: string
|
|
45
|
+
secret?: string;
|
|
46
46
|
/**
|
|
47
47
|
* The algorithm used to generate the TOTP.
|
|
48
48
|
* @default 'SHA1'
|
|
49
49
|
*/
|
|
50
|
-
algorithm?: string
|
|
50
|
+
algorithm?: string;
|
|
51
51
|
/**
|
|
52
52
|
* Number of digits in the generated token.
|
|
53
53
|
* @default 6
|
|
54
54
|
*/
|
|
55
|
-
digits?: number
|
|
55
|
+
digits?: number;
|
|
56
56
|
/**
|
|
57
57
|
* Validity period in seconds for the token.
|
|
58
58
|
* @default 30
|
|
59
59
|
*/
|
|
60
|
-
period?: number
|
|
60
|
+
period?: number;
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
export type TOTPCmdOptions = {
|
|
64
|
-
issuer?: string
|
|
65
|
-
label?: string
|
|
64
|
+
issuer?: string;
|
|
65
|
+
label?: string;
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
export function totp(secret?: string, options: TOTPOptions = {}) {
|
|
69
|
-
return new TOTP({ label:
|
|
69
|
+
return new TOTP({ label: 'SyntheticsTOTP', secret, ...options }).generate();
|
|
70
70
|
}
|
package/src/dsl/monitor.ts
CHANGED
|
@@ -158,6 +158,13 @@ export class Monitor {
|
|
|
158
158
|
.digest('base64');
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Returns the size of the monitor in bytes which is sent as payload to Kibana
|
|
163
|
+
*/
|
|
164
|
+
size() {
|
|
165
|
+
return JSON.stringify(this).length;
|
|
166
|
+
}
|
|
167
|
+
|
|
161
168
|
validate() {
|
|
162
169
|
const schedule = this.config.schedule;
|
|
163
170
|
if (ALLOWED_SCHEDULES.includes(schedule)) {
|
|
@@ -199,7 +199,9 @@ export class SyntheticsGenerator extends JavaScriptLanguageGenerator {
|
|
|
199
199
|
const offset = this.isProject ? 2 + stepIndent : 0 + stepIndent;
|
|
200
200
|
const formatter = new JavaScriptFormatter(offset);
|
|
201
201
|
|
|
202
|
-
const locators = actionInContext.frame.framePath.map(
|
|
202
|
+
const locators = actionInContext.frame.framePath.map(
|
|
203
|
+
selector => `.${super._asLocator(selector)}.contentFrame()`
|
|
204
|
+
);
|
|
203
205
|
const subject = `${pageAlias}${locators.join('')}`;
|
|
204
206
|
const signals = toSignalMap(action);
|
|
205
207
|
|
package/src/helpers.ts
CHANGED
|
@@ -41,7 +41,7 @@ import micromatch from 'micromatch';
|
|
|
41
41
|
|
|
42
42
|
const SEPARATOR = '\n';
|
|
43
43
|
|
|
44
|
-
export function noop() {
|
|
44
|
+
export function noop() {}
|
|
45
45
|
|
|
46
46
|
export function indent(lines: string, tab = ' ') {
|
|
47
47
|
return lines.replace(/^/gm, tab);
|
|
@@ -152,8 +152,8 @@ export function findPkgJsonByTraversing(resolvePath, cwd) {
|
|
|
152
152
|
if (resolvePath === parentDirectory) {
|
|
153
153
|
throw red(
|
|
154
154
|
`Could not find package.json file in: "${cwd}"\n` +
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
`It is recommended to run the agent in an NPM project.\n` +
|
|
156
|
+
`You can create one by running "npm init -y" in the project folder.`
|
|
157
157
|
);
|
|
158
158
|
}
|
|
159
159
|
return findPkgJsonByTraversing(parentDirectory, cwd);
|
package/src/loader.ts
CHANGED
|
@@ -29,7 +29,7 @@ import { CliArgs } from './common_types';
|
|
|
29
29
|
import { step, journey } from './core';
|
|
30
30
|
import { log } from './core/logger';
|
|
31
31
|
import { expect } from './core/expect';
|
|
32
|
-
import * as mfa from
|
|
32
|
+
import * as mfa from './core/mfa';
|
|
33
33
|
import {
|
|
34
34
|
isDepInstalled,
|
|
35
35
|
isDirectory,
|
package/src/plugins/tracing.ts
CHANGED
|
@@ -38,7 +38,7 @@ export type TraceOptions = {
|
|
|
38
38
|
* https://chromedevtools.github.io/devtools-protocol/tot/Tracing/
|
|
39
39
|
*/
|
|
40
40
|
export class Tracing {
|
|
41
|
-
constructor(private driver: Driver, private options: TraceOptions) {
|
|
41
|
+
constructor(private driver: Driver, private options: TraceOptions) {}
|
|
42
42
|
|
|
43
43
|
async start() {
|
|
44
44
|
log(`Plugins: started collecting trace events`);
|
package/src/push/bundler.ts
CHANGED
|
@@ -24,25 +24,19 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import path from 'path';
|
|
27
|
-
import {
|
|
27
|
+
import { unlink, readFile } from 'fs/promises';
|
|
28
28
|
import { createWriteStream } from 'fs';
|
|
29
29
|
import * as esbuild from 'esbuild';
|
|
30
30
|
import archiver from 'archiver';
|
|
31
31
|
import { commonOptions } from '../core/transform';
|
|
32
32
|
import { SyntheticsBundlePlugin } from './plugin';
|
|
33
33
|
|
|
34
|
-
// 1500KB Max Gzipped limit for bundled code to be pushed as Kibana project monitors.
|
|
35
|
-
const SIZE_LIMIT_KB = 1500;
|
|
36
|
-
|
|
37
34
|
function relativeToCwd(entry: string) {
|
|
38
35
|
return path.relative(process.cwd(), entry);
|
|
39
36
|
}
|
|
40
37
|
|
|
41
38
|
export class Bundler {
|
|
42
|
-
|
|
43
|
-
constructor() {}
|
|
44
|
-
|
|
45
|
-
async prepare(absPath: string) {
|
|
39
|
+
async bundle(absPath: string) {
|
|
46
40
|
const options: esbuild.BuildOptions = {
|
|
47
41
|
...commonOptions(),
|
|
48
42
|
...{
|
|
@@ -59,56 +53,42 @@ export class Bundler {
|
|
|
59
53
|
if (result.errors.length > 0) {
|
|
60
54
|
throw result.errors;
|
|
61
55
|
}
|
|
62
|
-
|
|
56
|
+
return result.outputFiles[0].text;
|
|
63
57
|
}
|
|
64
58
|
|
|
65
|
-
async zip(
|
|
59
|
+
async zip(source: string, code: string, dest: string) {
|
|
66
60
|
return new Promise((fulfill, reject) => {
|
|
67
|
-
const output = createWriteStream(
|
|
61
|
+
const output = createWriteStream(dest);
|
|
68
62
|
const archive = archiver('zip', {
|
|
69
63
|
zlib: { level: 9 },
|
|
70
64
|
});
|
|
71
65
|
archive.on('error', reject);
|
|
72
66
|
output.on('close', fulfill);
|
|
73
67
|
archive.pipe(output);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
});
|
|
82
|
-
}
|
|
68
|
+
const relativePath = relativeToCwd(source);
|
|
69
|
+
// Date is fixed to Unix epoch so the file metadata is
|
|
70
|
+
// not modified everytime when files are bundled
|
|
71
|
+
archive.append(code, {
|
|
72
|
+
name: relativePath,
|
|
73
|
+
date: new Date('1970-01-01'),
|
|
74
|
+
});
|
|
83
75
|
archive.finalize();
|
|
84
76
|
});
|
|
85
77
|
}
|
|
86
78
|
|
|
87
79
|
async build(entry: string, output: string) {
|
|
88
|
-
await this.
|
|
89
|
-
await this.zip(output);
|
|
90
|
-
const
|
|
91
|
-
await this.checkSize(output);
|
|
80
|
+
const code = await this.bundle(entry);
|
|
81
|
+
await this.zip(entry, code, output);
|
|
82
|
+
const content = await this.encode(output);
|
|
92
83
|
await this.cleanup(output);
|
|
93
|
-
return
|
|
84
|
+
return content;
|
|
94
85
|
}
|
|
95
86
|
|
|
96
87
|
async encode(outputPath: string) {
|
|
97
88
|
return await readFile(outputPath, 'base64');
|
|
98
89
|
}
|
|
99
90
|
|
|
100
|
-
async checkSize(outputPath: string) {
|
|
101
|
-
const { size } = await stat(outputPath);
|
|
102
|
-
const sizeKb = size / 1024;
|
|
103
|
-
if (sizeKb > SIZE_LIMIT_KB) {
|
|
104
|
-
throw new Error(
|
|
105
|
-
`Bundled monitor code exceeds the recommended ${SIZE_LIMIT_KB}KB limit. Please check your dependencies and try again.`
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
91
|
async cleanup(outputPath: string) {
|
|
111
|
-
this.moduleMap = new Map<string, string>();
|
|
112
92
|
await unlink(outputPath);
|
|
113
93
|
}
|
|
114
94
|
}
|
package/src/push/index.ts
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import { readFile, writeFile } from 'fs/promises';
|
|
26
26
|
import { prompt } from 'enquirer';
|
|
27
|
-
import { bold,
|
|
27
|
+
import { bold, underline } from 'kleur/colors';
|
|
28
28
|
import {
|
|
29
29
|
getLocalMonitors,
|
|
30
30
|
buildMonitorSchema,
|
|
@@ -57,8 +57,11 @@ import {
|
|
|
57
57
|
isBulkAPISupported,
|
|
58
58
|
isLightweightMonitorSupported,
|
|
59
59
|
logDiff,
|
|
60
|
+
logGroups,
|
|
61
|
+
printBytes,
|
|
60
62
|
} from './utils';
|
|
61
|
-
import {
|
|
63
|
+
import { runLocal } from './run-local';
|
|
64
|
+
import { inDebugMode } from '../core/globals';
|
|
62
65
|
|
|
63
66
|
export async function push(monitors: Monitor[], options: PushOptions) {
|
|
64
67
|
if (parseInt(process.env.CHUNK_SIZE) > 250) {
|
|
@@ -84,7 +87,7 @@ export async function push(monitors: Monitor[], options: PushOptions) {
|
|
|
84
87
|
const { monitors: remote } = await bulkGetMonitors(options);
|
|
85
88
|
|
|
86
89
|
progress(`preparing ${monitors.length} monitors`);
|
|
87
|
-
const schemas = await buildMonitorSchema(monitors, true);
|
|
90
|
+
const { schemas, sizes } = await buildMonitorSchema(monitors, true);
|
|
88
91
|
const local = getLocalMonitors(schemas);
|
|
89
92
|
|
|
90
93
|
const { newIDs, changedIDs, removedIDs, unchangedIDs } = diffMonitorHashIDs(
|
|
@@ -92,6 +95,22 @@ export async function push(monitors: Monitor[], options: PushOptions) {
|
|
|
92
95
|
remote
|
|
93
96
|
);
|
|
94
97
|
logDiff(newIDs, changedIDs, removedIDs, unchangedIDs);
|
|
98
|
+
if (inDebugMode()) {
|
|
99
|
+
logGroups(sizes, newIDs, changedIDs, removedIDs, unchangedIDs);
|
|
100
|
+
// show bundle size for the whole project
|
|
101
|
+
let totalSize = 0;
|
|
102
|
+
for (const value of sizes.values()) {
|
|
103
|
+
totalSize += value;
|
|
104
|
+
}
|
|
105
|
+
progress('total size of the monitors payload is ' + printBytes(totalSize));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (options.dryRun) {
|
|
109
|
+
progress('Running browser monitors in dry run mode');
|
|
110
|
+
await runLocal(schemas);
|
|
111
|
+
progress('Dry run completed');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
95
114
|
|
|
96
115
|
const updatedMonitors = new Set<string>([...changedIDs, ...newIDs]);
|
|
97
116
|
if (updatedMonitors.size > 0) {
|
|
@@ -106,7 +125,6 @@ export async function push(monitors: Monitor[], options: PushOptions) {
|
|
|
106
125
|
}
|
|
107
126
|
|
|
108
127
|
if (removedIDs.size > 0) {
|
|
109
|
-
log(`deleting monitor ids: ${Array.from(removedIDs.keys()).join(', ')}`);
|
|
110
128
|
if (updatedMonitors.size === 0 && unchangedIDs.size === 0) {
|
|
111
129
|
await confirmDelete(
|
|
112
130
|
`Pushing without any monitors will delete all monitors associated with the project.\n Do you want to continue?`,
|
|
@@ -126,8 +144,7 @@ export async function push(monitors: Monitor[], options: PushOptions) {
|
|
|
126
144
|
);
|
|
127
145
|
}
|
|
128
146
|
}
|
|
129
|
-
|
|
130
|
-
done(`Pushed: ${grey(getMonitorManagementURL(options.url))}`);
|
|
147
|
+
done(`Pushed: ${underline(getMonitorManagementURL(options.url))} `);
|
|
131
148
|
}
|
|
132
149
|
|
|
133
150
|
async function confirmDelete(message: string, skip: boolean) {
|
|
@@ -223,8 +240,9 @@ export function validateSettings(opts: PushOptions) {
|
|
|
223
240
|
- CLI '--schedule <mins>'
|
|
224
241
|
- Config file 'monitors.schedule' field`;
|
|
225
242
|
} else if (opts.schedule && !ALLOWED_SCHEDULES.includes(opts.schedule)) {
|
|
226
|
-
reason = `Set default schedule(${
|
|
227
|
-
|
|
243
|
+
reason = `Set default schedule(${
|
|
244
|
+
opts.schedule
|
|
245
|
+
}) to one of the allowed values - ${ALLOWED_SCHEDULES.join(',')}`;
|
|
228
246
|
} else if (
|
|
229
247
|
(opts.locations ?? []).length > 0 &&
|
|
230
248
|
(opts?.playwrightOptions?.clientCertificates ?? []).filter(cert => {
|
|
@@ -301,7 +319,7 @@ export async function pushLegacy(monitors: Monitor[], options: PushOptions) {
|
|
|
301
319
|
let schemas: MonitorSchema[] = [];
|
|
302
320
|
if (monitors.length > 0) {
|
|
303
321
|
progress(`preparing ${monitors.length} monitors`);
|
|
304
|
-
schemas = await buildMonitorSchema(monitors, false);
|
|
322
|
+
({ schemas } = await buildMonitorSchema(monitors, false));
|
|
305
323
|
const chunks = getChunks(schemas, 10);
|
|
306
324
|
for (const chunk of chunks) {
|
|
307
325
|
await liveProgress(
|
|
@@ -320,7 +338,7 @@ export async function pushLegacy(monitors: Monitor[], options: PushOptions) {
|
|
|
320
338
|
`deleting all stale monitors`
|
|
321
339
|
);
|
|
322
340
|
|
|
323
|
-
done(`Pushed: ${
|
|
341
|
+
done(`Pushed: ${underline(getMonitorManagementURL(options.url))}`);
|
|
324
342
|
}
|
|
325
343
|
|
|
326
344
|
// prints warning if any of the monitors has throttling settings enabled during push
|
package/src/push/monitor.ts
CHANGED
|
@@ -42,6 +42,8 @@ import { isParamOptionSupported, normalizeMonitorName } from './utils';
|
|
|
42
42
|
|
|
43
43
|
// Allowed extensions for lightweight monitor files
|
|
44
44
|
const ALLOWED_LW_EXTENSIONS = ['.yml', '.yaml'];
|
|
45
|
+
// 1500kB Max Gzipped limit for bundled monitor code to be pushed as Kibana project monitors.
|
|
46
|
+
const SIZE_LIMIT_KB = 1500;
|
|
45
47
|
|
|
46
48
|
export type MonitorSchema = Omit<MonitorConfig, 'locations'> & {
|
|
47
49
|
locations: string[];
|
|
@@ -131,6 +133,7 @@ export async function buildMonitorSchema(monitors: Monitor[], isV2: boolean) {
|
|
|
131
133
|
await mkdir(bundlePath, { recursive: true });
|
|
132
134
|
const bundler = new Bundler();
|
|
133
135
|
const schemas: MonitorSchema[] = [];
|
|
136
|
+
const sizes: Map<string, number> = new Map();
|
|
134
137
|
|
|
135
138
|
for (const monitor of monitors) {
|
|
136
139
|
const { source, config, filter, type } = monitor;
|
|
@@ -148,6 +151,17 @@ export async function buildMonitorSchema(monitors: Monitor[], isV2: boolean) {
|
|
|
148
151
|
monitor.setContent(content);
|
|
149
152
|
Object.assign(schema, { content, filter });
|
|
150
153
|
}
|
|
154
|
+
const size = monitor.size();
|
|
155
|
+
const sizeKB = Math.round(size / 1000);
|
|
156
|
+
if (sizeKB > SIZE_LIMIT_KB) {
|
|
157
|
+
let outer = bold(
|
|
158
|
+
`Aborted: Bundled code ${sizeKB}kB exceeds the recommended ${SIZE_LIMIT_KB}kB limit. Please check the dependencies imported.\n`
|
|
159
|
+
);
|
|
160
|
+
const inner = `* ${config.id} - ${source.file}:${source.line}:${source.column}\n`;
|
|
161
|
+
outer += indent(inner);
|
|
162
|
+
throw red(outer);
|
|
163
|
+
}
|
|
164
|
+
sizes.set(config.id, size);
|
|
151
165
|
/**
|
|
152
166
|
* Generate hash only after the bundled content is created
|
|
153
167
|
* to capture code changes in imported files
|
|
@@ -159,7 +173,7 @@ export async function buildMonitorSchema(monitors: Monitor[], isV2: boolean) {
|
|
|
159
173
|
}
|
|
160
174
|
|
|
161
175
|
await rm(bundlePath, { recursive: true });
|
|
162
|
-
return schemas;
|
|
176
|
+
return { schemas, sizes };
|
|
163
177
|
}
|
|
164
178
|
|
|
165
179
|
export async function createLightweightMonitors(
|
|
@@ -225,9 +239,7 @@ export async function createLightweightMonitors(
|
|
|
225
239
|
const monitor = mergedConfig[i];
|
|
226
240
|
// Skip browser monitors from the YML files
|
|
227
241
|
if (monitor['type'] === 'browser') {
|
|
228
|
-
warn(
|
|
229
|
-
`Browser monitors from ${file} are skipped.`
|
|
230
|
-
);
|
|
242
|
+
warn(`Browser monitors from ${file} are skipped.`);
|
|
231
243
|
continue;
|
|
232
244
|
}
|
|
233
245
|
const { line, col } = lineCounter.linePos(offsets[i]);
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2020-present, Elastic NV
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in
|
|
14
|
+
* all copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22
|
+
* THE SOFTWARE.
|
|
23
|
+
*
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { execFileSync, spawn } from 'child_process';
|
|
27
|
+
import { rm, writeFile } from 'fs/promises';
|
|
28
|
+
import { createReadStream } from 'fs';
|
|
29
|
+
import { tmpdir } from 'os';
|
|
30
|
+
import { Extract } from 'unzip-stream';
|
|
31
|
+
import { red } from 'kleur/colors';
|
|
32
|
+
import { join } from 'path';
|
|
33
|
+
import { pathToFileURL } from 'url';
|
|
34
|
+
import { MonitorSchema } from './monitor';
|
|
35
|
+
|
|
36
|
+
async function unzipFile(zipPath, destination) {
|
|
37
|
+
return new Promise<void>((resolve, reject) => {
|
|
38
|
+
createReadStream(zipPath)
|
|
39
|
+
.pipe(Extract({ path: destination }))
|
|
40
|
+
.on('close', resolve)
|
|
41
|
+
.on('error', err =>
|
|
42
|
+
reject(new Error(`failed to extract zip ${zipPath} : ${err.message}`))
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function runNpmInstall(directory) {
|
|
48
|
+
return new Promise<void>((resolve, reject) => {
|
|
49
|
+
const flags = [
|
|
50
|
+
'--no-audit', // Prevent audit checks
|
|
51
|
+
'--no-update-notifier', // Prevent update checks
|
|
52
|
+
'--no-fund', // No need for package funding messages here
|
|
53
|
+
'--package-lock=false', // no need to write package lock here
|
|
54
|
+
'--progress=false', // no need to display progress
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const npmInstall = spawn('npm', ['install', ...flags], {
|
|
58
|
+
cwd: directory,
|
|
59
|
+
stdio: 'ignore',
|
|
60
|
+
});
|
|
61
|
+
npmInstall.on('close', code => {
|
|
62
|
+
if (code === 0) {
|
|
63
|
+
resolve();
|
|
64
|
+
} else {
|
|
65
|
+
reject(new Error(`npm install failed with exit code ${code}`));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
npmInstall.on('error', err =>
|
|
69
|
+
reject(new Error(`failed to setup: ${err.message}`))
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function runTest(directory, schema: MonitorSchema) {
|
|
75
|
+
return new Promise<void>((resolve, reject) => {
|
|
76
|
+
const runTest = spawn(
|
|
77
|
+
'npx',
|
|
78
|
+
[
|
|
79
|
+
'@elastic/synthetics',
|
|
80
|
+
'.',
|
|
81
|
+
'--playwright-options',
|
|
82
|
+
JSON.stringify(schema.playwrightOptions),
|
|
83
|
+
'--params',
|
|
84
|
+
JSON.stringify(schema.params),
|
|
85
|
+
],
|
|
86
|
+
{
|
|
87
|
+
cwd: directory,
|
|
88
|
+
stdio: 'inherit',
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
runTest.on('close', resolve);
|
|
93
|
+
runTest.on('error', err => {
|
|
94
|
+
reject(
|
|
95
|
+
new Error(`Failed to execute @elastic/synthetics : ${err.message}`)
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function writePkgJSON(dir: string, synthPath: string) {
|
|
102
|
+
const packageJsonContent = {
|
|
103
|
+
name: 'project-journey',
|
|
104
|
+
private: 'true',
|
|
105
|
+
dependencies: {
|
|
106
|
+
'@elastic/synthetics': pathToFileURL(synthPath),
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
await writeFile(
|
|
110
|
+
join(dir, 'package.json'),
|
|
111
|
+
JSON.stringify(packageJsonContent, null, 2),
|
|
112
|
+
'utf-8'
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function extract(
|
|
117
|
+
schema: MonitorSchema,
|
|
118
|
+
zipPath: string,
|
|
119
|
+
unzipPath: string
|
|
120
|
+
) {
|
|
121
|
+
if (schema.type !== 'browser') {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const content = schema.content;
|
|
125
|
+
await writeFile(zipPath, content, 'base64');
|
|
126
|
+
await unzipFile(zipPath, unzipPath);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function runLocal(schemas: MonitorSchema[]) {
|
|
130
|
+
// lookup installed bin path of a node module
|
|
131
|
+
const resolvedPath = execFileSync('which', ['elastic-synthetics'], {
|
|
132
|
+
encoding: 'utf8',
|
|
133
|
+
}).trim();
|
|
134
|
+
const synthPath = resolvedPath.replace(
|
|
135
|
+
join('bin', 'elastic-synthetics'),
|
|
136
|
+
join('lib', 'node_modules', '@elastic/synthetics')
|
|
137
|
+
);
|
|
138
|
+
const rand = Date.now();
|
|
139
|
+
const zipPath = join(tmpdir(), `synthetics-zip-${rand}.zip`);
|
|
140
|
+
const unzipPath = join(tmpdir(), `synthetics-unzip-${rand}`);
|
|
141
|
+
try {
|
|
142
|
+
for (const schema of schemas) {
|
|
143
|
+
await extract(schema, zipPath, unzipPath);
|
|
144
|
+
}
|
|
145
|
+
await writePkgJSON(unzipPath, synthPath);
|
|
146
|
+
await runNpmInstall(unzipPath);
|
|
147
|
+
// TODO: figure out a way to collect all params and Playwright options
|
|
148
|
+
await runTest(unzipPath, schemas[0]);
|
|
149
|
+
} catch (e) {
|
|
150
|
+
throw red(`Aborted: ${e.message}`);
|
|
151
|
+
} finally {
|
|
152
|
+
await rm(zipPath, { recursive: true, force: true });
|
|
153
|
+
await rm(unzipPath, { recursive: true, force: true });
|
|
154
|
+
}
|
|
155
|
+
}
|
package/src/push/utils.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
import semver from 'semver';
|
|
27
27
|
import { progress, removeTrailingSlash } from '../helpers';
|
|
28
|
-
import { green, red, grey, yellow } from 'kleur/colors';
|
|
28
|
+
import { green, red, grey, yellow, Colorize, bold } from 'kleur/colors';
|
|
29
29
|
import { PushOptions } from '../common_types';
|
|
30
30
|
import { Monitor } from '../dsl/monitor';
|
|
31
31
|
|
|
@@ -37,13 +37,59 @@ export function logDiff<T extends Set<string>>(
|
|
|
37
37
|
) {
|
|
38
38
|
progress(
|
|
39
39
|
'Monitor Diff: ' +
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
green(`Added(${newIDs.size}) `) +
|
|
41
|
+
yellow(`Updated(${changedIDs.size}) `) +
|
|
42
|
+
red(`Removed(${removedIDs.size}) `) +
|
|
43
|
+
grey(`Unchanged(${unchangedIDs.size})`)
|
|
44
44
|
);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
export function logGroups<T extends Set<string>>(
|
|
48
|
+
sizes: Map<string, number>,
|
|
49
|
+
newIDs: T,
|
|
50
|
+
changedIDs: T,
|
|
51
|
+
removedIDs: T,
|
|
52
|
+
unchangedIDs: T
|
|
53
|
+
) {
|
|
54
|
+
console.groupCollapsed();
|
|
55
|
+
logGroup(sizes, 'Added', newIDs, green);
|
|
56
|
+
logGroup(sizes, 'Updated', changedIDs, yellow);
|
|
57
|
+
logGroup(sizes, 'Removed', removedIDs, red);
|
|
58
|
+
logGroup(sizes, 'Unchanged', unchangedIDs, grey);
|
|
59
|
+
console.groupEnd();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function logGroup(
|
|
63
|
+
sizes: Map<string, number>,
|
|
64
|
+
name: string,
|
|
65
|
+
ids: Set<string>,
|
|
66
|
+
color: Colorize
|
|
67
|
+
) {
|
|
68
|
+
if (ids.size === 0) return;
|
|
69
|
+
// under collapsed group, so giving 2 space for padding
|
|
70
|
+
printLine(process.stdout.columns - 2);
|
|
71
|
+
console.groupCollapsed(color(bold(name)));
|
|
72
|
+
[...ids].forEach(id => {
|
|
73
|
+
console.log(grey(`- ${id} (${printBytes(sizes.get(id))})`));
|
|
74
|
+
});
|
|
75
|
+
console.groupEnd();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function printLine(length: number = process.stdout.columns) {
|
|
79
|
+
console.log(grey('-').repeat(length));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const BYTE_UNITS = ['B', 'kB', 'MB', 'GB', 'TB', 'PB'];
|
|
83
|
+
export function printBytes(bytes: number) {
|
|
84
|
+
if (bytes <= 0) return '0 B';
|
|
85
|
+
const exponent = Math.min(
|
|
86
|
+
Math.floor(Math.log10(bytes) / 3),
|
|
87
|
+
BYTE_UNITS.length - 1
|
|
88
|
+
);
|
|
89
|
+
bytes /= 1000 ** exponent;
|
|
90
|
+
return `${bytes.toFixed(1)} ${BYTE_UNITS[exponent]}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
47
93
|
export function getChunks<T>(arr: Array<T>, size: number): Array<T[]> {
|
|
48
94
|
const chunks = [];
|
|
49
95
|
for (let i = 0; i < arr.length; i += size) {
|