Skip to content

Commit a0ce7b5

Browse files
Spencerspalgerkibanamachine
authored
[kbn/optimizer][ci-stats] ship metrics separate from build (#90482)
Co-authored-by: spalger <spalger@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
1 parent c306a44 commit a0ce7b5

33 files changed

+518
-353
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,7 @@
558558
"@types/webpack": "^4.41.3",
559559
"@types/webpack-env": "^1.15.3",
560560
"@types/webpack-merge": "^4.1.5",
561+
"@types/webpack-sources": "^0.1.4",
561562
"@types/write-pkg": "^3.1.0",
562563
"@types/xml-crypto": "^1.4.1",
563564
"@types/xml2js": "^0.4.5",
@@ -843,6 +844,7 @@
843844
"webpack-cli": "^3.3.12",
844845
"webpack-dev-server": "^3.11.0",
845846
"webpack-merge": "^4.2.2",
847+
"webpack-sources": "^1.4.1",
846848
"write-pkg": "^4.0.0",
847849
"xml-crypto": "^2.0.0",
848850
"xmlbuilder": "13.0.2",

packages/kbn-dev-utils/src/ci_stats_reporter/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
*/
88

99
export * from './ci_stats_reporter';
10+
export * from './ship_ci_stats_cli';
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import Path from 'path';
10+
import Fs from 'fs';
11+
12+
import { CiStatsReporter } from './ci_stats_reporter';
13+
import { run, createFlagError } from '../run';
14+
15+
export function shipCiStatsCli() {
16+
run(
17+
async ({ log, flags }) => {
18+
let metricPaths = flags.metrics;
19+
if (typeof metricPaths === 'string') {
20+
metricPaths = [metricPaths];
21+
} else if (!Array.isArray(metricPaths) || !metricPaths.every((p) => typeof p === 'string')) {
22+
throw createFlagError('expected --metrics to be a string');
23+
}
24+
25+
const reporter = CiStatsReporter.fromEnv(log);
26+
for (const path of metricPaths) {
27+
// resolve path from CLI relative to CWD
28+
const abs = Path.resolve(path);
29+
const json = Fs.readFileSync(abs, 'utf8');
30+
await reporter.metrics(JSON.parse(json));
31+
log.success('shipped metrics from', path);
32+
}
33+
},
34+
{
35+
description: 'ship ci-stats which have been written to files',
36+
usage: `node scripts/ship_ci_stats`,
37+
log: {
38+
defaultLevel: 'debug',
39+
},
40+
flags: {
41+
string: ['metrics'],
42+
help: `
43+
--metrics [path] A path to a JSON file that includes metrics which should be sent. Multiple instances supported
44+
`,
45+
},
46+
}
47+
);
48+
}

packages/kbn-optimizer/src/cli.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@ import Path from 'path';
1212

1313
import { REPO_ROOT } from '@kbn/utils';
1414
import { lastValueFrom } from '@kbn/std';
15-
import { run, createFlagError, CiStatsReporter } from '@kbn/dev-utils';
15+
import { run, createFlagError } from '@kbn/dev-utils';
1616

1717
import { logOptimizerState } from './log_optimizer_state';
1818
import { OptimizerConfig } from './optimizer';
19-
import { reportOptimizerStats } from './report_optimizer_stats';
2019
import { runOptimizer } from './run_optimizer';
2120
import { validateLimitsForAllBundles, updateBundleLimits } from './limits';
2221

@@ -120,17 +119,7 @@ run(
120119
return;
121120
}
122121

123-
let update$ = runOptimizer(config);
124-
125-
if (reportStats) {
126-
const reporter = CiStatsReporter.fromEnv(log);
127-
128-
if (!reporter.isEnabled()) {
129-
log.warning('Unable to initialize CiStatsReporter from env');
130-
}
131-
132-
update$ = update$.pipe(reportOptimizerStats(reporter, config, log));
133-
}
122+
const update$ = runOptimizer(config);
134123

135124
await lastValueFrom(update$.pipe(logOptimizerState(log, config)));
136125

@@ -153,7 +142,6 @@ run(
153142
'cache',
154143
'profile',
155144
'inspect-workers',
156-
'report-stats',
157145
'validate-limits',
158146
'update-limits',
159147
],
@@ -179,7 +167,6 @@ run(
179167
--dist create bundles that are suitable for inclusion in the Kibana distributable, enabled when running with --update-limits
180168
--scan-dir add a directory to the list of directories scanned for plugins (specify as many times as necessary)
181169
--no-inspect-workers when inspecting the parent process, don't inspect the workers
182-
--report-stats attempt to report stats about this execution of the build to the kibana-ci-stats service using this name
183170
--validate-limits validate the limits.yml config to ensure that there are limits defined for every bundle
184171
--update-limits run a build and rewrite the limits file to include the current bundle sizes +5kb
185172
`,

packages/kbn-optimizer/src/common/bundle.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ it('creates cache keys', () => {
4242
"id": "bar",
4343
"manifestPath": undefined,
4444
"outputDir": "/foo/bar/target",
45+
"pageLoadAssetSizeLimit": undefined,
4546
"publicDirNames": Array [
4647
"public",
4748
],
@@ -79,6 +80,7 @@ it('parses bundles from JSON specs', () => {
7980
"id": "bar",
8081
"manifestPath": undefined,
8182
"outputDir": "/foo/bar/target",
83+
"pageLoadAssetSizeLimit": undefined,
8284
"publicDirNames": Array [
8385
"public",
8486
],

packages/kbn-optimizer/src/common/bundle.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export interface BundleSpec {
3636
readonly banner?: string;
3737
/** Absolute path to a kibana.json manifest file, if omitted we assume there are not dependenices */
3838
readonly manifestPath?: string;
39+
/** Maximum allowed page load asset size for the bundles page load asset */
40+
readonly pageLoadAssetSizeLimit?: number;
3941
}
4042

4143
export class Bundle {
@@ -63,6 +65,8 @@ export class Bundle {
6365
* Every bundle mentioned in the `requiredBundles` must be built together.
6466
*/
6567
public readonly manifestPath: BundleSpec['manifestPath'];
68+
/** Maximum allowed page load asset size for the bundles page load asset */
69+
public readonly pageLoadAssetSizeLimit: BundleSpec['pageLoadAssetSizeLimit'];
6670

6771
public readonly cache: BundleCache;
6872

@@ -75,8 +79,9 @@ export class Bundle {
7579
this.outputDir = spec.outputDir;
7680
this.manifestPath = spec.manifestPath;
7781
this.banner = spec.banner;
82+
this.pageLoadAssetSizeLimit = spec.pageLoadAssetSizeLimit;
7883

79-
this.cache = new BundleCache(Path.resolve(this.outputDir, '.kbn-optimizer-cache'));
84+
this.cache = new BundleCache(this.outputDir);
8085
}
8186

8287
/**
@@ -107,6 +112,7 @@ export class Bundle {
107112
outputDir: this.outputDir,
108113
manifestPath: this.manifestPath,
109114
banner: this.banner,
115+
pageLoadAssetSizeLimit: this.pageLoadAssetSizeLimit,
110116
};
111117
}
112118

@@ -222,6 +228,13 @@ export function parseBundles(json: string) {
222228
}
223229
}
224230

231+
const { pageLoadAssetSizeLimit } = spec;
232+
if (pageLoadAssetSizeLimit !== undefined) {
233+
if (!(typeof pageLoadAssetSizeLimit === 'number')) {
234+
throw new Error('`bundles[]` must have a numeric `pageLoadAssetSizeLimit` property');
235+
}
236+
}
237+
225238
return new Bundle({
226239
type,
227240
id,
@@ -231,6 +244,7 @@ export function parseBundles(json: string) {
231244
outputDir,
232245
banner,
233246
manifestPath,
247+
pageLoadAssetSizeLimit,
234248
});
235249
}
236250
);

packages/kbn-optimizer/src/common/bundle_cache.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ beforeEach(() => {
2525
});
2626

2727
it(`doesn't complain if files are not on disk`, () => {
28-
const cache = new BundleCache('/foo/bar.json');
28+
const cache = new BundleCache('/foo');
2929
expect(cache.get()).toEqual({});
3030
});
3131

3232
it(`updates files on disk when calling set()`, () => {
33-
const cache = new BundleCache('/foo/bar.json');
33+
const cache = new BundleCache('/foo');
3434
cache.set(SOME_STATE);
3535
expect(mockReadFileSync).not.toHaveBeenCalled();
3636
expect(mockMkdirSync.mock.calls).toMatchInlineSnapshot(`
@@ -46,7 +46,7 @@ it(`updates files on disk when calling set()`, () => {
4646
expect(mockWriteFileSync.mock.calls).toMatchInlineSnapshot(`
4747
Array [
4848
Array [
49-
"/foo/bar.json",
49+
"/foo/.kbn-optimizer-cache",
5050
"{
5151
\\"cacheKey\\": \\"abc\\",
5252
\\"files\\": [
@@ -61,7 +61,7 @@ it(`updates files on disk when calling set()`, () => {
6161
});
6262

6363
it(`serves updated state from memory`, () => {
64-
const cache = new BundleCache('/foo/bar.json');
64+
const cache = new BundleCache('/foo');
6565
cache.set(SOME_STATE);
6666
jest.clearAllMocks();
6767

@@ -72,7 +72,7 @@ it(`serves updated state from memory`, () => {
7272
});
7373

7474
it('reads state from disk on get() after refresh()', () => {
75-
const cache = new BundleCache('/foo/bar.json');
75+
const cache = new BundleCache('/foo');
7676
cache.set(SOME_STATE);
7777
cache.refresh();
7878
jest.clearAllMocks();
@@ -83,15 +83,15 @@ it('reads state from disk on get() after refresh()', () => {
8383
expect(mockReadFileSync.mock.calls).toMatchInlineSnapshot(`
8484
Array [
8585
Array [
86-
"/foo/bar.json",
86+
"/foo/.kbn-optimizer-cache",
8787
"utf8",
8888
],
8989
]
9090
`);
9191
});
9292

9393
it('provides accessors to specific state properties', () => {
94-
const cache = new BundleCache('/foo/bar.json');
94+
const cache = new BundleCache('/foo');
9595

9696
expect(cache.getModuleCount()).toBe(undefined);
9797
expect(cache.getReferencedFiles()).toEqual(undefined);

packages/kbn-optimizer/src/common/bundle_cache.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
import Fs from 'fs';
1010
import Path from 'path';
1111

12+
import webpack from 'webpack';
13+
import { RawSource } from 'webpack-sources';
14+
1215
export interface State {
1316
optimizerCacheKey?: unknown;
1417
cacheKey?: unknown;
@@ -20,13 +23,17 @@ export interface State {
2023

2124
const DEFAULT_STATE: State = {};
2225
const DEFAULT_STATE_JSON = JSON.stringify(DEFAULT_STATE);
26+
const CACHE_FILENAME = '.kbn-optimizer-cache';
2327

2428
/**
2529
* Helper to read and update metadata for bundles.
2630
*/
2731
export class BundleCache {
2832
private state: State | undefined = undefined;
29-
constructor(private readonly path: string | false) {}
33+
private readonly path: string | false;
34+
constructor(outputDir: string | false) {
35+
this.path = outputDir === false ? false : Path.resolve(outputDir, CACHE_FILENAME);
36+
}
3037

3138
refresh() {
3239
this.state = undefined;
@@ -63,6 +70,7 @@ export class BundleCache {
6370

6471
set(updated: State) {
6572
this.state = updated;
73+
6674
if (this.path) {
6775
const directory = Path.dirname(this.path);
6876
Fs.mkdirSync(directory, { recursive: true });
@@ -107,4 +115,16 @@ export class BundleCache {
107115
}
108116
}
109117
}
118+
119+
public writeWebpackAsset(compilation: webpack.compilation.Compilation) {
120+
if (!this.path) {
121+
return;
122+
}
123+
124+
const source = new RawSource(JSON.stringify(this.state, null, 2));
125+
126+
// see https://github.com/jantimon/html-webpack-plugin/blob/33d69f49e6e9787796402715d1b9cd59f80b628f/index.js#L266
127+
// @ts-expect-error undocumented, used to add assets to the output
128+
compilation.emitAsset(CACHE_FILENAME, source);
129+
}
110130
}

packages/kbn-optimizer/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@
99
export { OptimizerConfig } from './optimizer';
1010
export * from './run_optimizer';
1111
export * from './log_optimizer_state';
12-
export * from './report_optimizer_stats';
1312
export * from './node';
1413
export * from './limits';

packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap

Lines changed: 34 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)