Skip to content

Commit bb9ba10

Browse files
[9.0] [CI] Storybook parallel build (#234841) (#235436)
# Backport This will backport the following commits from `main` to `9.0`: - [[CI] Storybook parallel build (#234841)](#234841) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Brad White","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-09-17T23:26:47Z","message":"[CI] Storybook parallel build (#234841)\n\n## Summary\n\n- Part of elastic/kibana-operations#346 & elastic/kibana-operations#347\n- Closes #176500\n- Parallelize Storybook build\n- Migrate `gsutil` to `gcloud storage cp`\n\n### Testing\nI tried a few machine sizes to compare the build times:\n\n| Machine | Time | Reduction |\n|--------------|--------------|---------|\n| `n2-standard-32` | 9m 32s | 82% |\n| `n2-standard-16` | 15m 43s | 69% |\n| `n2-standard-8` | 19m 32s | 62% |\n| `n2-standard-4` | 38m 39s | 24% |\n| Sync `n2-standard-8` | 50m 40s | - |\n\nFor now we can stick with `n2-standard-8` since 20 minutes will be well\nunder the FTR times and gives a minor decrease in CI costs since we're\nusing the machine for less time.","sha":"29d189ff7df46d791d503311fff6c06f77c7a271","branchLabelMapping":{"^v9.2.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Operations","release_note:skip","ci:build-storybooks","backport:version","v9.2.0","v8.19.5","v9.0.8","v9.1.5"],"title":"[CI] Storybook parallel build","number":234841,"url":"https://github.com/elastic/kibana/pull/234841","mergeCommit":{"message":"[CI] Storybook parallel build (#234841)\n\n## Summary\n\n- Part of elastic/kibana-operations#346 & elastic/kibana-operations#347\n- Closes #176500\n- Parallelize Storybook build\n- Migrate `gsutil` to `gcloud storage cp`\n\n### Testing\nI tried a few machine sizes to compare the build times:\n\n| Machine | Time | Reduction |\n|--------------|--------------|---------|\n| `n2-standard-32` | 9m 32s | 82% |\n| `n2-standard-16` | 15m 43s | 69% |\n| `n2-standard-8` | 19m 32s | 62% |\n| `n2-standard-4` | 38m 39s | 24% |\n| Sync `n2-standard-8` | 50m 40s | - |\n\nFor now we can stick with `n2-standard-8` since 20 minutes will be well\nunder the FTR times and gives a minor decrease in CI costs since we're\nusing the machine for less time.","sha":"29d189ff7df46d791d503311fff6c06f77c7a271"}},"sourceBranch":"main","suggestedTargetBranches":["8.19","9.0","9.1"],"targetPullRequestStates":[{"branch":"main","label":"v9.2.0","branchLabelMappingKey":"^v9.2.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/234841","number":234841,"mergeCommit":{"message":"[CI] Storybook parallel build (#234841)\n\n## Summary\n\n- Part of elastic/kibana-operations#346 & elastic/kibana-operations#347\n- Closes #176500\n- Parallelize Storybook build\n- Migrate `gsutil` to `gcloud storage cp`\n\n### Testing\nI tried a few machine sizes to compare the build times:\n\n| Machine | Time | Reduction |\n|--------------|--------------|---------|\n| `n2-standard-32` | 9m 32s | 82% |\n| `n2-standard-16` | 15m 43s | 69% |\n| `n2-standard-8` | 19m 32s | 62% |\n| `n2-standard-4` | 38m 39s | 24% |\n| Sync `n2-standard-8` | 50m 40s | - |\n\nFor now we can stick with `n2-standard-8` since 20 minutes will be well\nunder the FTR times and gives a minor decrease in CI costs since we're\nusing the machine for less time.","sha":"29d189ff7df46d791d503311fff6c06f77c7a271"}},{"branch":"8.19","label":"v8.19.5","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"9.0","label":"v9.0.8","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"9.1","label":"v9.1.5","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Brad White <[email protected]>
1 parent be9e353 commit bb9ba10

File tree

3 files changed

+100
-33
lines changed

3 files changed

+100
-33
lines changed

.buildkite/package-lock.json

Lines changed: 32 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.buildkite/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
"js-yaml": "^4.1.0",
1515
"minimatch": "^5.0.1",
1616
"minimist": "^1.2.8",
17+
"p-limit": "^3.1.0",
1718
"tslib": "*"
1819
},
1920
"devDependencies": {
2021
"@types/jest": "^30.0.0",
2122
"@types/js-yaml": "^4.0.9",
2223
"@types/minimatch": "^3.0.5",
2324
"@types/minimist": "^1.2.5",
24-
"@types/node": "^15.12.2",
25+
"@types/node": "^22.17.1",
2526
"jest": "^30.0.3",
2627
"nock": "^12.0.2",
2728
"ts-jest": "^29.4.0",

.buildkite/scripts/steps/storybooks/build_and_upload.ts

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
import { execSync } from 'child_process';
10+
import { execSync, spawn } from 'child_process';
1111
import fs from 'fs';
1212
import path from 'path';
13+
import os from 'os';
14+
import pLimit from 'p-limit';
1315
import { storybookAliases } from '../../../../src/dev/storybook/aliases';
1416
import { getKibanaDir } from '#pipeline-utils';
1517

@@ -25,6 +27,42 @@ const STORYBOOK_BASE_URL = `${STORYBOOK_BUCKET_URL}`;
2527

2628
const exec = (...args: string[]) => execSync(args.join(' '), { stdio: 'inherit' });
2729

30+
const buildStorybook = (storybook: string): Promise<{ logs: string }> => {
31+
return new Promise((resolve, reject) => {
32+
const logsBuffer: string[] = [];
33+
const handleBufferChunk = (chunk: Buffer) => {
34+
logsBuffer.push(chunk.toString());
35+
};
36+
37+
const child = spawn('yarn', ['storybook', '--site', storybook], {
38+
stdio: 'pipe',
39+
env: {
40+
...process.env,
41+
STORYBOOK_BASE_URL,
42+
NODE_OPTIONS: '--max-old-space-size=6144',
43+
},
44+
});
45+
46+
child.stdout?.on('data', handleBufferChunk);
47+
child.stderr?.on('data', handleBufferChunk);
48+
49+
child.on('close', (code) => {
50+
if (code === 0) {
51+
logsBuffer.unshift(`--- ✅ ${storybook} storybook\n`);
52+
resolve({ logs: logsBuffer.join('') });
53+
} else {
54+
logsBuffer.unshift(`--- ❌ ${storybook} storybook\n`);
55+
reject(new Error(logsBuffer.join('')));
56+
}
57+
});
58+
59+
child.on('error', () => {
60+
logsBuffer.unshift(`--- ❌ ${storybook} storybook\n`);
61+
reject(new Error(logsBuffer.join('')));
62+
});
63+
});
64+
};
65+
2866
const ghStatus = (state: string, description: string) =>
2967
exec(
3068
`gh api "repos/elastic/kibana/statuses/${process.env.BUILDKITE_COMMIT}"`,
@@ -35,15 +73,23 @@ const ghStatus = (state: string, description: string) =>
3573
`--silent`
3674
);
3775

38-
const build = () => {
76+
const build = async () => {
3977
console.log('--- Building Storybooks');
4078

41-
for (const storybook of Object.keys(storybookAliases)) {
42-
exec(
43-
`STORYBOOK_BASE_URL=${STORYBOOK_BASE_URL}`,
44-
`NODE_OPTIONS=--max-old-space-size=6144`,
45-
`yarn storybook --site ${storybook}`
79+
const limit = pLimit(os.availableParallelism());
80+
const storybooks = Object.keys(storybookAliases);
81+
82+
try {
83+
const results = await Promise.all(
84+
storybooks.map((storybook) => limit(() => buildStorybook(storybook)))
4685
);
86+
87+
results.forEach(({ logs }) => {
88+
console.log(logs);
89+
});
90+
} catch (error) {
91+
console.error(error);
92+
throw error;
4793
}
4894
};
4995

@@ -84,8 +130,8 @@ const upload = () => {
84130
);
85131
exec(`
86132
${activateScript} gs://ci-artifacts.kibana.dev
87-
gsutil -h "Cache-Control:no-cache, max-age=0, no-transform" -q -m cp -r -z js,css,html,json,map,txt,svg '*' 'gs://${STORYBOOK_BUCKET}/${STORYBOOK_DIRECTORY}/'
88-
gsutil -h "Cache-Control:no-cache, max-age=0, no-transform" cp -z html 'index.html' 'gs://${STORYBOOK_BUCKET}/${STORYBOOK_DIRECTORY}/latest/'
133+
gcloud storage cp --cache-control="no-cache, max-age=0, no-transform" --gzip-local=js,css,html,json,map,txt,svg --recursive --no-user-output-enabled '*' 'gs://${STORYBOOK_BUCKET}/${STORYBOOK_DIRECTORY}/'
134+
gcloud storage cp --cache-control="no-cache, max-age=0, no-transform" --gzip-local=html --no-user-output-enabled 'index.html' 'gs://${STORYBOOK_BUCKET}/${STORYBOOK_DIRECTORY}/latest/'
89135
`);
90136

91137
if (process.env.BUILDKITE_PULL_REQUEST && process.env.BUILDKITE_PULL_REQUEST !== 'false') {
@@ -98,12 +144,14 @@ const upload = () => {
98144
}
99145
};
100146

101-
try {
102-
ghStatus('pending', 'Building Storybooks');
103-
build();
104-
upload();
105-
ghStatus('success', 'Storybooks built');
106-
} catch (error) {
107-
ghStatus('error', 'Building Storybooks failed');
108-
throw error;
109-
}
147+
(async () => {
148+
try {
149+
ghStatus('pending', 'Building Storybooks');
150+
await build();
151+
upload();
152+
ghStatus('success', 'Storybooks built');
153+
} catch (error) {
154+
ghStatus('error', 'Building Storybooks failed');
155+
throw error;
156+
}
157+
})();

0 commit comments

Comments
 (0)