Raise CSS gzip budget to 36 KiB to accommodate react-markdown #8
release.yml
on: push
Web UI Gates
39s
Matrix: build
Publish GitHub release (tags only)
0s
Annotations
2 errors and 1 warning
|
Web UI Gates
Process completed with exit code 1.
|
|
src/app/build-performance.test.ts > build performance baselines > wires deterministic gzip budget checks for embedded entry assets:
crates/orchestrator-web-server/web-ui/src/app/build-performance.test.ts#L37
AssertionError: expected '#!/usr/bin/env node\n\nimport { readF…' to contain 'CSS_GZIP_BUDGET_BYTES = 24 * 1024'
- Expected
+ Received
- CSS_GZIP_BUDGET_BYTES = 24 * 1024
+ #!/usr/bin/env node
+
+ import { readFileSync, statSync } from "node:fs";
+ import { basename, relative, resolve } from "node:path";
+ import { gzipSync } from "node:zlib";
+
+ const JS_GZIP_BUDGET_BYTES = 110 * 1024;
+ const CSS_GZIP_BUDGET_BYTES = 36 * 1024;
+
+ const embeddedDirPath = resolve(import.meta.dirname, "..", "..", "embedded");
+ const embeddedIndexPath = resolve(embeddedDirPath, "index.html");
+ const failures = [];
+
+ const embeddedIndexSource = readEmbeddedIndexSource(embeddedIndexPath, failures);
+ const scriptAssetPaths = embeddedIndexSource
+ ? extractReferencedAssets(
+ embeddedIndexSource,
+ /<script\b[^>]*\bsrc=["']([^"']+\.js(?:[?#][^"']*)?)["'][^>]*><\/script>/g,
+ )
+ : [];
+ const stylesheetAssetPaths = embeddedIndexSource
+ ? extractReferencedAssets(
+ embeddedIndexSource,
+ /<link\b[^>]*\brel=["']stylesheet["'][^>]*\bhref=["']([^"']+\.css(?:[?#][^"']*)?)["'][^>]*>/g,
+ )
+ : [];
+
+ const jsEntryAsset = pickEntryAsset(scriptAssetPaths, ".js");
+ const cssEntryAsset = pickEntryAsset(stylesheetAssetPaths, ".css");
+
+ if (!jsEntryAsset) {
+ failures.push("Missing referenced JS entry asset in embedded/index.html");
+ }
+
+ if (!cssEntryAsset) {
+ failures.push("Missing referenced CSS entry asset in embedded/index.html");
+ }
+
+ if (jsEntryAsset) {
+ const jsResult = buildAssetResult(jsEntryAsset, JS_GZIP_BUDGET_BYTES);
+ if (jsResult.kind === "error") {
+ failures.push(jsResult.message);
+ } else {
+ reportAssetResult("JS", jsResult.result);
+ if (jsResult.result.isOverBudget) {
+ failures.push(
+ `JS entry asset is over budget (${formatBytes(jsResult.result.gzipBytes)} > ${formatBytes(JS_GZIP_BUDGET_BYTES)})`,
+ );
+ }
+ }
+ }
+
+ if (cssEntryAsset) {
+ const cssResult = buildAssetResult(cssEntryAsset, CSS_GZIP_BUDGET_BYTES);
+ if (cssResult.kind === "error") {
+ failures.push(cssResult.message);
+ } else {
+ reportAssetResult("CSS", cssResult.result);
+ if (cssResult.result.isOverBudget) {
+ failures.push(
+ `CSS entry asset is over budget (${formatBytes(cssResult.result.gzipBytes)} > ${formatBytes(CSS_GZIP_BUDGET_BYTES)})`,
+ );
+ }
+ }
+ }
+
+ if (failures.length > 0) {
+ for (const failure of failures) {
+ console.error(`[budget:fail] ${failure}`);
+ }
+ process.exit(1);
+ }
+
+ console.log("[budget:ok] Embedded entry assets meet gzip budgets");
+
+ function readEmbeddedIndexSource(indexPath, failures) {
+ try {
+ return readFileSync(indexPath, "utf8");
+ } catch (error) {
+ failures.push(
+ `Unable to read embedded/index.html at ${indexPath}. Run \`npm run build\` before budget checks.`,
+ );
+ if (error instanceof Error && error.message) {
+ failures.push(`embedded/index.html read error: ${error.message}`);
+ }
+ return null;
+ }
+ }
+
+ function extractReferencedAssets(source, pattern) {
+ const referencedAssets = [];
+ let match = pattern.exec(source);
+
+ while (match) {
+ referencedAssets.push(match[1]);
+ match = pattern.exec(source);
+ }
+
+ return Array.from(new Set(referencedAssets));
+ }
+
+ function pickEntryAsset(assetPaths, extension) {
+ if (assetPaths.length === 0) {
+ return null;
+ }
+
+ const namedEntry = assetPaths.find((assetPath) => {
+ const fileName = basename(assetPath);
+ return fileName.startsWith("index-") && fileName.endsWith(extension);
+ });
+
+ return namedEntry ?? assetPaths[0];
+ }
+
+ function buildAssetResult(assetPath, budgetBytes) {
+ const relativeAssetPath = normalizeAssetPath(assetPath);
+ const absoluteAssetPath = resolve(embeddedDirPath, relativeAssetPath);
+ if (!isInsideDirectory(embeddedDirPath, absoluteAssetPath)) {
+ return {
+ kind: "error",
+ message: `Referenced asset resolves outside embedded directory: ${assetPath}`,
+
|
|
Web UI Gates
Node.js 20 actions are deprecated. The following actions are running on Node.js 20 and may not work as expected: actions/checkout@v4, actions/setup-node@v4, actions/upload-artifact@v4. Actions will be forced to run with Node.js 24 by default starting June 2nd, 2026. Please check if updated versions of these actions are available that support Node.js 24. To opt into Node.js 24 now, set the FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true environment variable on the runner or in your workflow file. Once Node.js 24 becomes the default, you can temporarily opt out by setting ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
|