mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
[kbn/optimizer] report sizes of assets produced by optimizer (#71319)
* Revert "Report page load asset size (#66224)" This reverts commit6f57fa0b2d
. * [kbn/optimizer] report sizes of assets produced by optimizer * coalese the fast-glob versions we're using to prevent additional installs * update kbn/pm dist * Revert "update kbn/pm dist" This reverts commit68e24f0fad
. * Revert "coalese the fast-glob versions we're using to prevent additional installs" This reverts commit4201fb60b6
. * remove fast-glob, just recursivly call readdirSync() * update integration tests to use new chunk filename Co-authored-by: spalger <spalger@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
58b4127b68
commit
a0f7dced13
21 changed files with 86 additions and 2995 deletions
1
Jenkinsfile
vendored
1
Jenkinsfile
vendored
|
@ -42,7 +42,6 @@ kibanaPipeline(timeoutMinutes: 155, checkPrChanges: true, setCommitStatus: true)
|
||||||
'xpack-ciGroup10': kibanaPipeline.xpackCiGroupProcess(10),
|
'xpack-ciGroup10': kibanaPipeline.xpackCiGroupProcess(10),
|
||||||
'xpack-accessibility': kibanaPipeline.functionalTestProcess('xpack-accessibility', './test/scripts/jenkins_xpack_accessibility.sh'),
|
'xpack-accessibility': kibanaPipeline.functionalTestProcess('xpack-accessibility', './test/scripts/jenkins_xpack_accessibility.sh'),
|
||||||
'xpack-savedObjectsFieldMetrics': kibanaPipeline.functionalTestProcess('xpack-savedObjectsFieldMetrics', './test/scripts/jenkins_xpack_saved_objects_field_metrics.sh'),
|
'xpack-savedObjectsFieldMetrics': kibanaPipeline.functionalTestProcess('xpack-savedObjectsFieldMetrics', './test/scripts/jenkins_xpack_saved_objects_field_metrics.sh'),
|
||||||
// 'xpack-pageLoadMetrics': kibanaPipeline.functionalTestProcess('xpack-pageLoadMetrics', './test/scripts/jenkins_xpack_page_load_metrics.sh'),
|
|
||||||
'xpack-securitySolutionCypress': { processNumber ->
|
'xpack-securitySolutionCypress': { processNumber ->
|
||||||
whenChanged(['x-pack/plugins/security_solution/', 'x-pack/test/security_solution_cypress/']) {
|
whenChanged(['x-pack/plugins/security_solution/', 'x-pack/test/security_solution_cypress/']) {
|
||||||
kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypress', './test/scripts/jenkins_security_solution_cypress.sh')(processNumber)
|
kibanaPipeline.functionalTestProcess('xpack-securitySolutionCypress', './test/scripts/jenkins_security_solution_cypress.sh')(processNumber)
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -220,7 +220,7 @@ it('prepares assets for distribution', async () => {
|
||||||
|
|
||||||
expectFileMatchesSnapshotWithCompression('plugins/foo/target/public/foo.plugin.js', 'foo bundle');
|
expectFileMatchesSnapshotWithCompression('plugins/foo/target/public/foo.plugin.js', 'foo bundle');
|
||||||
expectFileMatchesSnapshotWithCompression(
|
expectFileMatchesSnapshotWithCompression(
|
||||||
'plugins/foo/target/public/1.plugin.js',
|
'plugins/foo/target/public/foo.chunk.1.js',
|
||||||
'foo async bundle'
|
'foo async bundle'
|
||||||
);
|
);
|
||||||
expectFileMatchesSnapshotWithCompression('plugins/bar/target/public/bar.plugin.js', 'bar bundle');
|
expectFileMatchesSnapshotWithCompression('plugins/bar/target/public/bar.plugin.js', 'bar bundle');
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import Fs from 'fs';
|
||||||
|
import Path from 'path';
|
||||||
|
|
||||||
import { materialize, mergeMap, dematerialize } from 'rxjs/operators';
|
import { materialize, mergeMap, dematerialize } from 'rxjs/operators';
|
||||||
import { CiStatsReporter } from '@kbn/dev-utils';
|
import { CiStatsReporter } from '@kbn/dev-utils';
|
||||||
|
|
||||||
|
@ -24,6 +27,32 @@ import { OptimizerUpdate$ } from './run_optimizer';
|
||||||
import { OptimizerState, OptimizerConfig } from './optimizer';
|
import { OptimizerState, OptimizerConfig } from './optimizer';
|
||||||
import { pipeClosure } from './common';
|
import { pipeClosure } from './common';
|
||||||
|
|
||||||
|
const flatten = <T>(arr: Array<T | T[]>): T[] =>
|
||||||
|
arr.reduce((acc: T[], item) => acc.concat(item), []);
|
||||||
|
|
||||||
|
interface Entry {
|
||||||
|
relPath: string;
|
||||||
|
stats: Fs.Stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFiles = (dir: string, parent?: string) =>
|
||||||
|
flatten(
|
||||||
|
Fs.readdirSync(dir).map((name): Entry | Entry[] => {
|
||||||
|
const absPath = Path.join(dir, name);
|
||||||
|
const relPath = parent ? Path.join(parent, name) : name;
|
||||||
|
const stats = Fs.statSync(absPath);
|
||||||
|
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
return getFiles(absPath, relPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
relPath,
|
||||||
|
stats,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
export function reportOptimizerStats(reporter: CiStatsReporter, config: OptimizerConfig) {
|
export function reportOptimizerStats(reporter: CiStatsReporter, config: OptimizerConfig) {
|
||||||
return pipeClosure((update$: OptimizerUpdate$) => {
|
return pipeClosure((update$: OptimizerUpdate$) => {
|
||||||
let lastState: OptimizerState | undefined;
|
let lastState: OptimizerState | undefined;
|
||||||
|
@ -36,16 +65,55 @@ export function reportOptimizerStats(reporter: CiStatsReporter, config: Optimize
|
||||||
|
|
||||||
if (n.kind === 'C' && lastState) {
|
if (n.kind === 'C' && lastState) {
|
||||||
await reporter.metrics(
|
await reporter.metrics(
|
||||||
|
flatten(
|
||||||
config.bundles.map((bundle) => {
|
config.bundles.map((bundle) => {
|
||||||
// make the cache read from the cache file since it was likely updated by the worker
|
// make the cache read from the cache file since it was likely updated by the worker
|
||||||
bundle.cache.refresh();
|
bundle.cache.refresh();
|
||||||
|
|
||||||
return {
|
const outputFiles = getFiles(bundle.outputDir).filter(
|
||||||
|
(file) => !(file.relPath.startsWith('.') || file.relPath.endsWith('.map'))
|
||||||
|
);
|
||||||
|
|
||||||
|
const entryName = `${bundle.id}.${bundle.type}.js`;
|
||||||
|
const entry = outputFiles.find((f) => f.relPath === entryName);
|
||||||
|
if (!entry) {
|
||||||
|
throw new Error(
|
||||||
|
`Unable to find bundle entry named [${entryName}] in [${bundle.outputDir}]`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const chunkPrefix = `${bundle.id}.chunk.`;
|
||||||
|
const asyncChunks = outputFiles.filter((f) => f.relPath.startsWith(chunkPrefix));
|
||||||
|
const miscFiles = outputFiles.filter(
|
||||||
|
(f) => f !== entry && !asyncChunks.includes(f)
|
||||||
|
);
|
||||||
|
const sumSize = (files: Entry[]) =>
|
||||||
|
files.reduce((acc: number, f) => acc + f.stats!.size, 0);
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
group: `@kbn/optimizer bundle module count`,
|
group: `@kbn/optimizer bundle module count`,
|
||||||
id: bundle.id,
|
id: bundle.id,
|
||||||
value: bundle.cache.getModuleCount() || 0,
|
value: bundle.cache.getModuleCount() || 0,
|
||||||
};
|
},
|
||||||
|
{
|
||||||
|
group: `page load bundle size`,
|
||||||
|
id: bundle.id,
|
||||||
|
value: entry.stats!.size,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: `async chunks size`,
|
||||||
|
id: bundle.id,
|
||||||
|
value: sumSize(asyncChunks),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: `miscellaneous assets size`,
|
||||||
|
id: bundle.id,
|
||||||
|
value: sumSize(miscFiles),
|
||||||
|
},
|
||||||
|
];
|
||||||
})
|
})
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,8 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker:
|
||||||
|
|
||||||
output: {
|
output: {
|
||||||
path: bundle.outputDir,
|
path: bundle.outputDir,
|
||||||
filename: `[name].${bundle.type}.js`,
|
filename: `${bundle.id}.${bundle.type}.js`,
|
||||||
|
chunkFilename: `${bundle.id}.chunk.[id].js`,
|
||||||
devtoolModuleFilenameTemplate: (info) =>
|
devtoolModuleFilenameTemplate: (info) =>
|
||||||
`/${bundle.type}:${bundle.id}/${Path.relative(
|
`/${bundle.type}:${bundle.id}/${Path.relative(
|
||||||
bundle.sourceRoot,
|
bundle.sourceRoot,
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
"@types/joi": "^13.4.2",
|
"@types/joi": "^13.4.2",
|
||||||
"@types/lodash": "^4.14.155",
|
"@types/lodash": "^4.14.155",
|
||||||
"@types/parse-link-header": "^1.0.0",
|
"@types/parse-link-header": "^1.0.0",
|
||||||
"@types/puppeteer": "^3.0.0",
|
|
||||||
"@types/strip-ansi": "^5.2.1",
|
"@types/strip-ansi": "^5.2.1",
|
||||||
"@types/xml2js": "^0.4.5",
|
"@types/xml2js": "^0.4.5",
|
||||||
"diff": "^4.0.1"
|
"diff": "^4.0.1"
|
||||||
|
@ -31,7 +30,6 @@
|
||||||
"joi": "^13.5.2",
|
"joi": "^13.5.2",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"parse-link-header": "^1.0.1",
|
"parse-link-header": "^1.0.1",
|
||||||
"puppeteer": "^3.3.0",
|
|
||||||
"rxjs": "^6.5.5",
|
"rxjs": "^6.5.5",
|
||||||
"strip-ansi": "^5.2.0",
|
"strip-ansi": "^5.2.0",
|
||||||
"tar-fs": "^1.16.3",
|
"tar-fs": "^1.16.3",
|
||||||
|
|
|
@ -60,4 +60,3 @@ export { makeJunitReportPath } from './junit_report_path';
|
||||||
export { CI_PARALLEL_PROCESS_PREFIX } from './ci_parallel_process_prefix';
|
export { CI_PARALLEL_PROCESS_PREFIX } from './ci_parallel_process_prefix';
|
||||||
|
|
||||||
export * from './functional_test_runner';
|
export * from './functional_test_runner';
|
||||||
export * from './page_load_metrics';
|
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ToolingLog } from '@kbn/dev-utils';
|
|
||||||
import { NavigationOptions, createUrl, navigateToApps } from './navigation';
|
|
||||||
|
|
||||||
export async function capturePageLoadMetrics(log: ToolingLog, options: NavigationOptions) {
|
|
||||||
const responsesByPageView = await navigateToApps(log, options);
|
|
||||||
|
|
||||||
const assetSizeMeasurements = new Map<string, number[]>();
|
|
||||||
|
|
||||||
const numberOfPagesVisited = responsesByPageView.size;
|
|
||||||
|
|
||||||
for (const [, frameResponses] of responsesByPageView) {
|
|
||||||
for (const [, { url, dataLength }] of frameResponses) {
|
|
||||||
if (url.length === 0) {
|
|
||||||
throw new Error('navigateToApps(); failed to identify the url of the request');
|
|
||||||
}
|
|
||||||
if (assetSizeMeasurements.has(url)) {
|
|
||||||
assetSizeMeasurements.set(url, [dataLength].concat(assetSizeMeasurements.get(url) || []));
|
|
||||||
} else {
|
|
||||||
assetSizeMeasurements.set(url, [dataLength]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.from(assetSizeMeasurements.entries())
|
|
||||||
.map(([url, measurements]) => {
|
|
||||||
const baseUrl = createUrl('/', options.appConfig.url);
|
|
||||||
const relativeUrl = url
|
|
||||||
// remove the baseUrl (expect the trailing slash) to make url relative
|
|
||||||
.replace(baseUrl.slice(0, -1), '')
|
|
||||||
// strip the build number from asset urls
|
|
||||||
.replace(/^\/\d+\//, '/');
|
|
||||||
return [relativeUrl, measurements] as const;
|
|
||||||
})
|
|
||||||
.filter(([url, measurements]) => {
|
|
||||||
if (measurements.length !== numberOfPagesVisited) {
|
|
||||||
// ignore urls seen only on some pages
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url.startsWith('data:')) {
|
|
||||||
// ignore data urls since they are already counted by other assets
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url.startsWith('/api/') || url.startsWith('/internal/')) {
|
|
||||||
// ignore api requests since they don't have deterministic sizes
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const allMetricsAreEqual = measurements.every((x, i) =>
|
|
||||||
i === 0 ? true : x === measurements[i - 1]
|
|
||||||
);
|
|
||||||
if (!allMetricsAreEqual) {
|
|
||||||
throw new Error(`measurements for url [${url}] are not equal [${measurements.join(',')}]`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.map(([url, measurements]) => {
|
|
||||||
return { group: 'page load asset size', id: url, value: measurements[0] };
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Url from 'url';
|
|
||||||
|
|
||||||
import { run, createFlagError } from '@kbn/dev-utils';
|
|
||||||
import { resolve, basename } from 'path';
|
|
||||||
import { capturePageLoadMetrics } from './capture_page_load_metrics';
|
|
||||||
|
|
||||||
const defaultScreenshotsDir = resolve(__dirname, 'screenshots');
|
|
||||||
|
|
||||||
export function runPageLoadMetricsCli() {
|
|
||||||
run(
|
|
||||||
async ({ flags, log }) => {
|
|
||||||
const kibanaUrl = flags['kibana-url'];
|
|
||||||
if (!kibanaUrl || typeof kibanaUrl !== 'string') {
|
|
||||||
throw createFlagError('Expect --kibana-url to be a string');
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedUrl = Url.parse(kibanaUrl);
|
|
||||||
|
|
||||||
const [username, password] = parsedUrl.auth
|
|
||||||
? parsedUrl.auth.split(':')
|
|
||||||
: [flags.username, flags.password];
|
|
||||||
|
|
||||||
if (typeof username !== 'string' || typeof password !== 'string') {
|
|
||||||
throw createFlagError(
|
|
||||||
'Mising username and/or password, either specify in --kibana-url or pass --username and --password'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const headless = !flags.head;
|
|
||||||
|
|
||||||
const screenshotsDir = flags.screenshotsDir || defaultScreenshotsDir;
|
|
||||||
|
|
||||||
if (typeof screenshotsDir !== 'string' || screenshotsDir === basename(screenshotsDir)) {
|
|
||||||
throw createFlagError('Expect screenshotsDir to be valid path string');
|
|
||||||
}
|
|
||||||
|
|
||||||
const metrics = await capturePageLoadMetrics(log, {
|
|
||||||
headless,
|
|
||||||
appConfig: {
|
|
||||||
url: kibanaUrl,
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
},
|
|
||||||
screenshotsDir,
|
|
||||||
});
|
|
||||||
for (const metric of metrics) {
|
|
||||||
log.info(`${metric.id}: ${metric.value}`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: `Loads several pages with Puppeteer to capture the size of assets`,
|
|
||||||
flags: {
|
|
||||||
string: ['kibana-url', 'username', 'password', 'screenshotsDir'],
|
|
||||||
boolean: ['head'],
|
|
||||||
default: {
|
|
||||||
username: 'elastic',
|
|
||||||
password: 'changeme',
|
|
||||||
debug: true,
|
|
||||||
screenshotsDir: defaultScreenshotsDir,
|
|
||||||
},
|
|
||||||
help: `
|
|
||||||
--kibana-url Url for Kibana we should connect to, can include login info
|
|
||||||
--head Run puppeteer with graphical user interface
|
|
||||||
--username Set username, defaults to 'elastic'
|
|
||||||
--password Set password, defaults to 'changeme'
|
|
||||||
--screenshotsDir Set screenshots directory, defaults to '${defaultScreenshotsDir}'
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface ResponseReceivedEvent {
|
|
||||||
frameId: string;
|
|
||||||
loaderId: string;
|
|
||||||
requestId: string;
|
|
||||||
response: Record<string, any>;
|
|
||||||
timestamp: number;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DataReceivedEvent {
|
|
||||||
encodedDataLength: number;
|
|
||||||
dataLength: number;
|
|
||||||
requestId: string;
|
|
||||||
timestamp: number;
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from './cli';
|
|
||||||
export { capturePageLoadMetrics } from './capture_page_load_metrics';
|
|
|
@ -1,164 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Fs from 'fs';
|
|
||||||
import Url from 'url';
|
|
||||||
import puppeteer from 'puppeteer';
|
|
||||||
import { resolve } from 'path';
|
|
||||||
import { ToolingLog } from '@kbn/dev-utils';
|
|
||||||
import { ResponseReceivedEvent, DataReceivedEvent } from './event';
|
|
||||||
|
|
||||||
export interface NavigationOptions {
|
|
||||||
headless: boolean;
|
|
||||||
appConfig: { url: string; username: string; password: string };
|
|
||||||
screenshotsDir: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NavigationResults = Map<string, Map<string, FrameResponse>>;
|
|
||||||
|
|
||||||
interface FrameResponse {
|
|
||||||
url: string;
|
|
||||||
dataLength: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
function joinPath(pathA: string, pathB: string) {
|
|
||||||
return `${pathA.endsWith('/') ? pathA.slice(0, -1) : pathA}/${
|
|
||||||
pathB.startsWith('/') ? pathB.slice(1) : pathB
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createUrl(path: string, url: string) {
|
|
||||||
const baseUrl = Url.parse(url);
|
|
||||||
return Url.format({
|
|
||||||
protocol: baseUrl.protocol,
|
|
||||||
hostname: baseUrl.hostname,
|
|
||||||
port: baseUrl.port,
|
|
||||||
pathname: joinPath(baseUrl.pathname || '', path),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loginToKibana(
|
|
||||||
log: ToolingLog,
|
|
||||||
browser: puppeteer.Browser,
|
|
||||||
options: NavigationOptions
|
|
||||||
) {
|
|
||||||
log.debug(`log in to the app..`);
|
|
||||||
const page = await browser.newPage();
|
|
||||||
const loginUrl = createUrl('/login', options.appConfig.url);
|
|
||||||
await page.goto(loginUrl, {
|
|
||||||
waitUntil: 'networkidle0',
|
|
||||||
});
|
|
||||||
await page.type('[data-test-subj="loginUsername"]', options.appConfig.username);
|
|
||||||
await page.type('[data-test-subj="loginPassword"]', options.appConfig.password);
|
|
||||||
await page.click('[data-test-subj="loginSubmit"]');
|
|
||||||
await page.waitForNavigation({ waitUntil: 'networkidle0' });
|
|
||||||
await page.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function navigateToApps(log: ToolingLog, options: NavigationOptions) {
|
|
||||||
const browser = await puppeteer.launch({ headless: options.headless, args: ['--no-sandbox'] });
|
|
||||||
const devToolsResponses: NavigationResults = new Map();
|
|
||||||
const apps = [
|
|
||||||
{ path: '/app/discover', locator: '[data-test-subj="discover-sidebar"]' },
|
|
||||||
{ path: '/app/home', locator: '[data-test-subj="homeApp"]' },
|
|
||||||
{ path: '/app/canvas', locator: '[data-test-subj="create-workpad-button"]' },
|
|
||||||
{ path: '/app/maps', locator: '[title="Maps"]' },
|
|
||||||
{ path: '/app/apm', locator: '[data-test-subj="apmMainContainer"]' },
|
|
||||||
];
|
|
||||||
|
|
||||||
await loginToKibana(log, browser, options);
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
apps.map(async (app) => {
|
|
||||||
const page = await browser.newPage();
|
|
||||||
page.setCacheEnabled(false);
|
|
||||||
page.setDefaultNavigationTimeout(0);
|
|
||||||
const frameResponses = new Map<string, FrameResponse>();
|
|
||||||
devToolsResponses.set(app.path, frameResponses);
|
|
||||||
|
|
||||||
const client = await page.target().createCDPSession();
|
|
||||||
await client.send('Network.enable');
|
|
||||||
|
|
||||||
function getRequestData(requestId: string) {
|
|
||||||
if (!frameResponses.has(requestId)) {
|
|
||||||
frameResponses.set(requestId, { url: '', dataLength: 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
return frameResponses.get(requestId)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
client.on('Network.responseReceived', (event: ResponseReceivedEvent) => {
|
|
||||||
getRequestData(event.requestId).url = event.response.url;
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on('Network.dataReceived', (event: DataReceivedEvent) => {
|
|
||||||
getRequestData(event.requestId).dataLength += event.dataLength;
|
|
||||||
});
|
|
||||||
|
|
||||||
const url = createUrl(app.path, options.appConfig.url);
|
|
||||||
log.debug(`goto ${url}`);
|
|
||||||
await page.goto(url, {
|
|
||||||
waitUntil: 'networkidle0',
|
|
||||||
});
|
|
||||||
|
|
||||||
let readyAttempt = 0;
|
|
||||||
let selectorFound = false;
|
|
||||||
while (!selectorFound) {
|
|
||||||
readyAttempt += 1;
|
|
||||||
try {
|
|
||||||
await page.waitForSelector(app.locator, { timeout: 5000 });
|
|
||||||
selectorFound = true;
|
|
||||||
} catch (error) {
|
|
||||||
log.error(
|
|
||||||
`Page '${app.path}' was not loaded properly, unable to find '${
|
|
||||||
app.locator
|
|
||||||
}', url: ${page.url()}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (readyAttempt < 6) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const failureDir = resolve(options.screenshotsDir, 'failure');
|
|
||||||
const screenshotPath = resolve(
|
|
||||||
failureDir,
|
|
||||||
`${app.path.slice(1).split('/').join('_')}_navigation.png`
|
|
||||||
);
|
|
||||||
Fs.mkdirSync(failureDir, { recursive: true });
|
|
||||||
|
|
||||||
await page.bringToFront();
|
|
||||||
await page.screenshot({
|
|
||||||
path: screenshotPath,
|
|
||||||
type: 'png',
|
|
||||||
fullPage: true,
|
|
||||||
});
|
|
||||||
log.debug(`Saving screenshot to ${screenshotPath}`);
|
|
||||||
|
|
||||||
throw new Error(`Page load timeout: ${app.path} not loaded after 30 seconds`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await page.close();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
await browser.close();
|
|
||||||
|
|
||||||
return devToolsResponses;
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
require('../src/setup_node_env');
|
|
||||||
require('@kbn/test').runPageLoadMetricsCli();
|
|
|
@ -1,9 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
source test/scripts/jenkins_test_setup_xpack.sh
|
|
||||||
|
|
||||||
checks-reporter-with-killswitch "Capture Kibana page load metrics" \
|
|
||||||
node scripts/functional_tests \
|
|
||||||
--debug --bail \
|
|
||||||
--kibana-install-dir "$installDir" \
|
|
||||||
--config test/page_load_metrics/config.ts;
|
|
|
@ -17,6 +17,9 @@ tar -xzf "$linuxBuild" -C "$installDir" --strip=1
|
||||||
cd "$KIBANA_DIR"
|
cd "$KIBANA_DIR"
|
||||||
source "test/scripts/jenkins_xpack_saved_objects_field_metrics.sh"
|
source "test/scripts/jenkins_xpack_saved_objects_field_metrics.sh"
|
||||||
|
|
||||||
|
cd "$KIBANA_DIR"
|
||||||
|
source "test/scripts/jenkins_xpack_saved_objects_field_metrics.sh"
|
||||||
|
|
||||||
echo " -> running visual regression tests from x-pack directory"
|
echo " -> running visual regression tests from x-pack directory"
|
||||||
cd "$XPACK_DIR"
|
cd "$XPACK_DIR"
|
||||||
yarn percy exec -t 10000 -- -- \
|
yarn percy exec -t 10000 -- -- \
|
||||||
|
|
1
x-pack/.gitignore
vendored
1
x-pack/.gitignore
vendored
|
@ -3,7 +3,6 @@
|
||||||
/target
|
/target
|
||||||
/test/functional/failure_debug
|
/test/functional/failure_debug
|
||||||
/test/functional/screenshots
|
/test/functional/screenshots
|
||||||
/test/page_load_metrics/screenshots
|
|
||||||
/test/functional/apps/reporting/reports/session
|
/test/functional/apps/reporting/reports/session
|
||||||
/test/reporting/configs/failure_debug/
|
/test/reporting/configs/failure_debug/
|
||||||
/plugins/reporting/.chromium/
|
/plugins/reporting/.chromium/
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { resolve } from 'path';
|
|
||||||
|
|
||||||
import { FtrConfigProviderContext } from '@kbn/test/types/ftr';
|
|
||||||
import { PuppeteerTestRunner } from './runner';
|
|
||||||
|
|
||||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|
||||||
const kibanaCommonTestsConfig = await readConfigFile(
|
|
||||||
require.resolve('../../../test/common/config.js')
|
|
||||||
);
|
|
||||||
const xpackFunctionalTestsConfig = await readConfigFile(
|
|
||||||
require.resolve('../functional/config.js')
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...kibanaCommonTestsConfig.getAll(),
|
|
||||||
|
|
||||||
testRunner: PuppeteerTestRunner,
|
|
||||||
|
|
||||||
esArchiver: {
|
|
||||||
directory: resolve(__dirname, 'es_archives'),
|
|
||||||
},
|
|
||||||
|
|
||||||
screenshots: {
|
|
||||||
directory: resolve(__dirname, 'screenshots'),
|
|
||||||
},
|
|
||||||
|
|
||||||
esTestCluster: {
|
|
||||||
...xpackFunctionalTestsConfig.get('esTestCluster'),
|
|
||||||
serverArgs: [...xpackFunctionalTestsConfig.get('esTestCluster.serverArgs')],
|
|
||||||
},
|
|
||||||
|
|
||||||
kbnTestServer: {
|
|
||||||
...xpackFunctionalTestsConfig.get('kbnTestServer'),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { CiStatsReporter } from '@kbn/dev-utils';
|
|
||||||
import { capturePageLoadMetrics } from '@kbn/test';
|
|
||||||
// @ts-ignore not TS yet
|
|
||||||
import getUrl from '../../../src/test_utils/get_url';
|
|
||||||
|
|
||||||
import { FtrProviderContext } from './../functional/ftr_provider_context';
|
|
||||||
|
|
||||||
export async function PuppeteerTestRunner({ getService }: FtrProviderContext) {
|
|
||||||
const log = getService('log');
|
|
||||||
const config = getService('config');
|
|
||||||
const esArchiver = getService('esArchiver');
|
|
||||||
|
|
||||||
await esArchiver.load('default');
|
|
||||||
const metrics = await capturePageLoadMetrics(log, {
|
|
||||||
headless: true,
|
|
||||||
appConfig: {
|
|
||||||
url: getUrl.baseUrl(config.get('servers.kibana')),
|
|
||||||
username: config.get('servers.kibana.username'),
|
|
||||||
password: config.get('servers.kibana.password'),
|
|
||||||
},
|
|
||||||
screenshotsDir: config.get('screenshots.directory'),
|
|
||||||
});
|
|
||||||
const reporter = CiStatsReporter.fromEnv(log);
|
|
||||||
|
|
||||||
log.debug('Report page load asset size');
|
|
||||||
await reporter.metrics(metrics);
|
|
||||||
}
|
|
83
yarn.lock
83
yarn.lock
|
@ -5619,13 +5619,6 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/puppeteer@^3.0.0":
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-3.0.0.tgz#24cdcc131e319477608d893f0017e08befd70423"
|
|
||||||
integrity sha512-59+fkfHHXHzX5rgoXIMnZyzum7ZLx/Wc3fhsOduFThpTpKbzzdBHMZsrkKGLunimB4Ds/tI5lXTRLALK8Mmnhg==
|
|
||||||
dependencies:
|
|
||||||
"@types/node" "*"
|
|
||||||
|
|
||||||
"@types/q@^1.5.1":
|
"@types/q@^1.5.1":
|
||||||
version "1.5.2"
|
version "1.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
|
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
|
||||||
|
@ -8700,15 +8693,6 @@ bl@^3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
readable-stream "^3.0.1"
|
readable-stream "^3.0.1"
|
||||||
|
|
||||||
bl@^4.0.1:
|
|
||||||
version "4.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a"
|
|
||||||
integrity sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==
|
|
||||||
dependencies:
|
|
||||||
buffer "^5.5.0"
|
|
||||||
inherits "^2.0.4"
|
|
||||||
readable-stream "^3.4.0"
|
|
||||||
|
|
||||||
blob@0.0.5:
|
blob@0.0.5:
|
||||||
version "0.0.5"
|
version "0.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683"
|
resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683"
|
||||||
|
@ -9215,14 +9199,6 @@ buffer@^5.1.0, buffer@^5.2.0:
|
||||||
base64-js "^1.0.2"
|
base64-js "^1.0.2"
|
||||||
ieee754 "^1.1.4"
|
ieee754 "^1.1.4"
|
||||||
|
|
||||||
buffer@^5.2.1, buffer@^5.5.0:
|
|
||||||
version "5.6.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786"
|
|
||||||
integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==
|
|
||||||
dependencies:
|
|
||||||
base64-js "^1.0.2"
|
|
||||||
ieee754 "^1.1.4"
|
|
||||||
|
|
||||||
builtin-modules@^1.0.0:
|
builtin-modules@^1.0.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
|
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
|
||||||
|
@ -17675,7 +17651,7 @@ inflight@^1.0.4:
|
||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
inherits@2, inherits@2.0.4, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4:
|
inherits@2, inherits@2.0.4, inherits@~2.0.3, inherits@~2.0.4:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
@ -21893,11 +21869,6 @@ mixin-object@^2.0.1:
|
||||||
for-in "^0.1.3"
|
for-in "^0.1.3"
|
||||||
is-extendable "^0.1.1"
|
is-extendable "^0.1.1"
|
||||||
|
|
||||||
mkdirp-classic@^0.5.2:
|
|
||||||
version "0.5.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
|
|
||||||
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
|
|
||||||
|
|
||||||
mkdirp@0.5.1:
|
mkdirp@0.5.1:
|
||||||
version "0.5.1"
|
version "0.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||||
|
@ -25075,22 +25046,6 @@ puppeteer@^2.0.0:
|
||||||
rimraf "^2.6.1"
|
rimraf "^2.6.1"
|
||||||
ws "^6.1.0"
|
ws "^6.1.0"
|
||||||
|
|
||||||
puppeteer@^3.3.0:
|
|
||||||
version "3.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-3.3.0.tgz#95839af9fdc0aa4de7e5ee073a4c0adeb9e2d3d7"
|
|
||||||
integrity sha512-23zNqRltZ1PPoK28uRefWJ/zKb5Jhnzbbwbpcna2o5+QMn17F0khq5s1bdH3vPlyj+J36pubccR8wiNA/VE0Vw==
|
|
||||||
dependencies:
|
|
||||||
debug "^4.1.0"
|
|
||||||
extract-zip "^2.0.0"
|
|
||||||
https-proxy-agent "^4.0.0"
|
|
||||||
mime "^2.0.3"
|
|
||||||
progress "^2.0.1"
|
|
||||||
proxy-from-env "^1.0.0"
|
|
||||||
rimraf "^3.0.2"
|
|
||||||
tar-fs "^2.0.0"
|
|
||||||
unbzip2-stream "^1.3.3"
|
|
||||||
ws "^7.2.3"
|
|
||||||
|
|
||||||
q@^1.1.2:
|
q@^1.1.2:
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||||
|
@ -29745,16 +29700,6 @@ tar-fs@^1.16.3:
|
||||||
pump "^1.0.0"
|
pump "^1.0.0"
|
||||||
tar-stream "^1.1.2"
|
tar-stream "^1.1.2"
|
||||||
|
|
||||||
tar-fs@^2.0.0:
|
|
||||||
version "2.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.0.tgz#d1cdd121ab465ee0eb9ccde2d35049d3f3daf0d5"
|
|
||||||
integrity sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==
|
|
||||||
dependencies:
|
|
||||||
chownr "^1.1.1"
|
|
||||||
mkdirp-classic "^0.5.2"
|
|
||||||
pump "^3.0.0"
|
|
||||||
tar-stream "^2.0.0"
|
|
||||||
|
|
||||||
tar-stream@^1.1.2, tar-stream@^1.5.2:
|
tar-stream@^1.1.2, tar-stream@^1.5.2:
|
||||||
version "1.5.5"
|
version "1.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.5.tgz#5cad84779f45c83b1f2508d96b09d88c7218af55"
|
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.5.tgz#5cad84779f45c83b1f2508d96b09d88c7218af55"
|
||||||
|
@ -29765,17 +29710,6 @@ tar-stream@^1.1.2, tar-stream@^1.5.2:
|
||||||
readable-stream "^2.0.0"
|
readable-stream "^2.0.0"
|
||||||
xtend "^4.0.0"
|
xtend "^4.0.0"
|
||||||
|
|
||||||
tar-stream@^2.0.0:
|
|
||||||
version "2.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.2.tgz#6d5ef1a7e5783a95ff70b69b97455a5968dc1325"
|
|
||||||
integrity sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==
|
|
||||||
dependencies:
|
|
||||||
bl "^4.0.1"
|
|
||||||
end-of-stream "^1.4.1"
|
|
||||||
fs-constants "^1.0.0"
|
|
||||||
inherits "^2.0.3"
|
|
||||||
readable-stream "^3.1.1"
|
|
||||||
|
|
||||||
tar-stream@^2.1.0:
|
tar-stream@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3"
|
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3"
|
||||||
|
@ -30061,7 +29995,7 @@ through2@~2.0.3:
|
||||||
readable-stream "~2.3.6"
|
readable-stream "~2.3.6"
|
||||||
xtend "~4.0.1"
|
xtend "~4.0.1"
|
||||||
|
|
||||||
through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8, through@~2.3.4, through@~2.3.6, through@~2.3.8:
|
through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@~2.3.4, through@~2.3.6, through@~2.3.8:
|
||||||
version "2.3.8"
|
version "2.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||||
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
||||||
|
@ -31257,14 +31191,6 @@ unbzip2-stream@^1.0.9:
|
||||||
buffer "^3.0.1"
|
buffer "^3.0.1"
|
||||||
through "^2.3.6"
|
through "^2.3.6"
|
||||||
|
|
||||||
unbzip2-stream@^1.3.3:
|
|
||||||
version "1.4.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.2.tgz#84eb9e783b186d8fb397515fbb656f312f1a7dbf"
|
|
||||||
integrity sha512-pZMVAofMrrHX6Ik39hCk470kulCbmZ2SWfQLPmTWqfJV/oUm0gn1CblvHdUu4+54Je6Jq34x8kY6XjTy6dMkOg==
|
|
||||||
dependencies:
|
|
||||||
buffer "^5.2.1"
|
|
||||||
through "^2.3.8"
|
|
||||||
|
|
||||||
unc-path-regex@^0.1.2:
|
unc-path-regex@^0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
|
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
|
||||||
|
@ -33215,11 +33141,6 @@ ws@^7.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
async-limiter "^1.0.0"
|
async-limiter "^1.0.0"
|
||||||
|
|
||||||
ws@^7.2.3:
|
|
||||||
version "7.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd"
|
|
||||||
integrity sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==
|
|
||||||
|
|
||||||
ws@~3.3.1:
|
ws@~3.3.1:
|
||||||
version "3.3.3"
|
version "3.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue