Skip to content

Speed up the local docs preview workflow - #4206

Open
Mpdreamz wants to merge 5 commits into
mainfrom
fix/speed-up-preview-ci
Open

Mpdreamz wants to merge 5 commits into
mainfrom
fix/speed-up-preview-ci

Conversation

@Mpdreamz

@Mpdreamz Mpdreamz commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

The `Docs preview (local)` workflow took 6m32s per run; this cut brings the two slow steps — validate redirect rules (~117 s, of which 0.154 s was actual work) and the S3 upload (~145 s for 2.0 GiB) — down by fixing a NuGet cache miss, concentrating build cost, and pre-compressing the upload.

Affects: Deploys & previews, Automation

Prompt summary: The local docs preview workflow run 35741705709 took 6m32s. Two bottlenecks dominated: five separate `dotnet run` calls each paying MSBuild evaluation overhead (one absorbing a full restore+build), and an S3 upload of ~2.0 GiB of uncompressed HTML. The ask was to fix both without changing the runner label.

Why

`NUGET_PACKAGES` was not set in `docs-preview-local.yml`, so the 918 MB NuGet cache restored into a directory that `dotnet` never read — a full restore from nuget.org followed on every run. Five separate `dotnet run` calls each paid ~8 s of MSBuild evaluation. The first call also absorbed restore and build, so a 0.154 s validation appeared as a 2-minute step in the job log. The S3 upload sent ~2.0 GiB of uncompressed bytes because each of the 4,029 API operation pages is ~420 KB — 89% sidebar `

`.

What

NuGet cache miss

The `NUGET_PACKAGES` env var now propagates from `bootstrap/action.yml` before the `actions/cache` step, so every workflow that uses the action gets the correct path. The redundant `env:` in `ci.yml` and `smoke-test.yml` stays — same value, no harm.

Explicit build step

An explicit `dotnet build -c release src/tooling/docs-builder` step in `docs-preview-local.yml` concentrates restore and build cost under its own name. All five subsequent `dotnet run` calls pass `--no-build -c release`, which skips MSBuild evaluation entirely. The `-c release` flag also avoids the Debug-only `Elastic.Documentation.Api` project reference that inflates Debug builds.

Pre-compressed S3 upload

The `Upload to S3` step gzips all non-zip files in place with `xargs -P "$(nproc)"` before calling `aws s3 sync`, using `mktemp` for atomic writes. Zip bundles (`llm.zip`, `okf.zip`) are excluded from compression and synced separately without `content-encoding: gzip` so clients receive raw zip bytes. `links.json` and `redirects.json` are stashed to `/tmp` before compression; the `Update Link Index` action and the redirect deploy step read from `/tmp` so they always get plain JSON. CloudFront forwards pre-compressed objects untouched; the wire cost drops from ~2.0 GiB to ~120 MB.

Verify

Push this branch and read the job log on the resulting PR preview run. The `Validate redirect rules` step should drop from ~117 s to single-digit seconds. The `Upload to S3` step should drop from ~145 s and ship far fewer bytes.

Stack: 1 of 5. #4207 follows.

🤖 Generated with Claude Code

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: the new in-place gzip step compresses redirects.json before the CloudFront KVS redirect deployment step, so that step reads compressed bytes instead of JSON.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No new actionable findings; approving.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes due to a high-severity regression in the preview workflow that publishes compressed bytes as links.json for the link-index upload path.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

# Pre-compress the entire output tree in place so aws s3 sync ships ~120 MB instead of ~2 GiB.
# --content-encoding gzip applies sync-wide, so one pass keeps --delete semantics correct.
# gzip -n omits the timestamp so identical content produces identical bytes (deterministic).
find .artifacts/docs/html -type f -print0 \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[HIGH] In-place gzip also compresses links.json, breaking link-index publication

This step now rewrites every file under .artifacts/docs/html in place, so links.json is gzip bytes by the time Update Link Index runs later in the job. That action uploads .artifacts/docs/html/links.json as .../links.json via aws s3 cp without setting Content-Encoding, so consumers fetch compressed bytes while expecting plain JSON.

A concrete failure path is a normal push run: the upload succeeds, then downstream readers that deserialize the link index as JSON receive a gzip payload and fail parse. Preserve an uncompressed links.json for the update-link-index step (similar to redirects.json), or run link-index upload before the in-place compression pass.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve: no new actionable findings.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

Mpdreamz and others added 3 commits September 29, 2026 13:50
The `Docs preview (local)` workflow takes ~6m32s; two steps dominate.

#### Fix NUGET_PACKAGES so the cache is actually used

The bootstrap action caches `.nuget/packages` but never sets the
`NUGET_PACKAGES` env var, so `dotnet` ignores the cache and does a full
restore from nuget.org on every run. Adding the var to `bootstrap/action.yml`
fixes this for every consumer at once: `docs-preview-local.yml`,
`assembler-preview.yml`, and external repos that pin to this action.

#### Separate build from validation

Five consecutive `dotnet run` calls each pay ~8 s of MSBuild evaluation,
with the first also absorbing restore and build. An explicit
`dotnet build -c release src/tooling/docs-builder` step runs once before
any `dotnet run` call. All `dotnet run` calls become
`dotnet run --no-build -c release` so subsequent steps share the build
artifact instead of re-evaluating.

The release config also drops the Debug-only `Elastic.Documentation.Api`
project reference and skips the `curb` MSBuild rewrite that runs across
~25 projects in Debug mode.

#### Upload pre-compressed objects to preview S3

`aws s3 sync` ships raw bytes; the 4,029 API operation pages average ~420 KB
each, driving the output tree to ~2 GiB. CloudFront compresses on delivery,
but the upload cost is paid in full on every run.

The upload step now gzips the entire output tree in parallel before syncing,
then passes `--content-encoding gzip` so every object carries the header.
CloudFront forwards pre-compressed objects untouched, preserving brotli
negotiation for clients that ask for it — the flag only disables CloudFront's
own compression pass. Expected upload: ~120 MB instead of ~2 GiB.

One pass with `--content-encoding gzip` is intentional: the flag applies
sync-wide, so splitting into text vs binary would need two passes and would
break `--delete` (each pass deletes keys the other pass owns).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The in-place gzip pass compressed redirects.json alongside all other
output files. The subsequent Deploy redirects step read it as plain
JSON and got gzip bytes instead, breaking the redirects update.

Copy redirects.json to /tmp before compression and point the deploy
step at the uncompressed copy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…index upload

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GitHub Actions runs bash -e but not pipefail by default. In
'find ... | xargs gzip', the pipeline exit status comes from xargs, so a
partial find failure is masked. Without pipefail, some files can be left
uncompressed while aws s3 sync still applies --content-encoding gzip,
publishing non-gzip bytes with incorrect encoding metadata.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Site: replace Parcel with Vite for frontend builds

Vite eliminates the Windows TEMP workaround Parcel required and cuts
cold npm ci cost by caching node_modules in the bootstrap action.
SkipNpmBuild and DesignTimeBuild MSBuild hooks prevent redundant npm
runs in IDE and --no-build scenarios. IgnoreStandardErrorWarningFormat
on the npm run build exec prevents MSBuild from misclassifying Vite
chunk names (EuiIconWarning, EuiIconError) as build diagnostics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* CI: include .nvmrc in node_modules cache key and check all built CSS in SkipNpmBuild guard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Site: don't delete stable named outputs in CleanGeneratedStaticAssets

Removes main.js, styles.css, assembler.css, isolated.css, codex.css and their
map files from the pre-build clean target. Deleting declared NpmRunBuild outputs
before the incremental output check defeats MSBuild's up-to-date detection, so
npm run build ran on every dotnet build even when no source files changed.

The clean target now only removes content-hashed chunk-* and asset-* files from
prior Vite runs, which do need cleaning to avoid accumulation across builds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: upgrade Vite to 8.x and companion plugins to latest

Bumps `vite` from 6.3 to 8.3.1, `@vitejs/plugin-react` from 5.1 to
6.1.1, and `vite-plugin-svgr` from 4.3 to 5.2.0. Build and all 321
Jest tests pass unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

This branch was successfully deployed

1 active (outdated) deployment
assembler-preview — 0010105c Deployed Sep 29, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automation packaging, ci/cd.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants