Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/actions/bootstrap/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ outputs:
runs:
using: "composite"
steps:
- name: Set NUGET_PACKAGES path
shell: bash
run: echo "NUGET_PACKAGES=${{ github.workspace }}/.nuget/packages" >> $GITHUB_ENV # zizmor: ignore[github-env]

- uses: actions/cache@v6
with:
path: ${{ github.workspace }}/.nuget/packages
Expand Down Expand Up @@ -53,3 +57,10 @@ runs:
cache: npm
cache-dependency-path: src/Elastic.Documentation.Site/package-lock.json
node-version-file: .nvmrc

- uses: actions/cache@v6
with:
path: src/Elastic.Documentation.Site/node_modules
key: node-modules-${{ runner.os }}-${{ hashFiles('src/Elastic.Documentation.Site/package-lock.json', '.nvmrc') }}
restore-keys: |
node-modules-${{ runner.os }}-
7 changes: 0 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,6 @@ jobs:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: 'Windows only, set TEMP to the same drive'
if: ${{ matrix.os == 'windows-latest' }}
# temporary waiting for https://github.com/parcel-bundler/parcel/pull/10095 to fix
run: |
mkdir "D:\\Temp"
echo "TEMP=D:\\Temp" >> $env:GITHUB_ENV

- name: Bootstrap Action Workspace
id: bootstrap
uses: ./.github/actions/bootstrap
Expand Down
42 changes: 35 additions & 7 deletions .github/workflows/docs-preview-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -299,14 +299,22 @@ jobs:
&& (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
uses: elastic/docs-builder/.github/actions/bootstrap@main

- name: Build
if: >
env.MATCH == 'true'
&& needs.check.outputs.any_modified != 'false'
&& (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
run: |
dotnet build -c release src/tooling/docs-builder

- name: 'Validate redirect rules'
if: >
env.MATCH == 'true'
&& needs.check.outputs.any_modified != 'false'
&& (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
continue-on-error: true
run: |
dotnet run --project src/tooling/docs-builder -- diff validate
dotnet run --no-build -c release --project src/tooling/docs-builder -- diff validate

- name: Build documentation
id: internal-docs-build
Expand All @@ -315,7 +323,7 @@ jobs:
&& needs.check.outputs.any_modified != 'false'
&& (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
run: |
dotnet run --project src/tooling/docs-builder -- --strict --path-prefix "${PATH_PREFIX}"
dotnet run --no-build -c release --project src/tooling/docs-builder -- --strict --path-prefix "${PATH_PREFIX}"

- name: 'Validate inbound links'
if: >
Expand All @@ -325,7 +333,7 @@ jobs:
&& steps.internal-docs-build.outputs.skip != 'true'
&& needs.check.outputs.any_modified != 'false'
run: |
dotnet run --project src/tooling/docs-builder -- inbound-links validate-link-reference
dotnet run --no-build -c release --project src/tooling/docs-builder -- inbound-links validate-link-reference

- name: 'Validate local path prefixes against those claimed by global navigation.yml'
id: internal-validate-path-prefixes
Expand All @@ -337,7 +345,7 @@ jobs:
&& steps.internal-docs-build.outcome != 'skipped'
&& needs.check.outputs.any_modified != 'false'
run: |
dotnet run --project src/tooling/docs-builder -- assembler navigation validate-link-reference
dotnet run --no-build -c release --project src/tooling/docs-builder -- assembler navigation validate-link-reference

- uses: elastic/docs-builder/.github/actions/aws-auth@main
if: >
Expand All @@ -361,7 +369,25 @@ jobs:
AWS_RETRY_MODE: standard
AWS_MAX_ATTEMPTS: 6
run: |
aws s3 sync .artifacts/docs/html "s3://elastic-docs-v3-website-preview${PATH_PREFIX}" --delete --no-follow-symlinks
# Stash plain-JSON files read by downstream steps before compression rewrites them.
[ -f .artifacts/docs/html/redirects.json ] \
&& cp .artifacts/docs/html/redirects.json /tmp/redirects.json
[ -f .artifacts/docs/html/links.json ] \
&& cp .artifacts/docs/html/links.json /tmp/links.json

# Pre-compress all non-zip files in place; zip bundles (llm.zip, okf.zip) are already
# compressed and must not be served with content-encoding:gzip or clients can't decode them.
# gzip -n omits the timestamp so identical content produces identical bytes (deterministic).
find .artifacts/docs/html -type f ! -name "*.zip" -print0 \
| xargs -0 -P "$(nproc)" -I{} sh -c 'tmp=$(mktemp "$1.tmp.XXXXXX") && gzip -9 -n -c "$1" > "$tmp" && mv -f "$tmp" "$1"' _ {}

# Sync compressed files (all except .zip) with gzip content-encoding.
aws s3 sync .artifacts/docs/html "s3://elastic-docs-v3-website-preview${PATH_PREFIX}" \
--delete --no-follow-symlinks --content-encoding gzip --exclude "*.zip"
# Sync zip downloads separately without content-encoding so clients receive raw zip bytes.
aws s3 sync .artifacts/docs/html "s3://elastic-docs-v3-website-preview${PATH_PREFIX}" \
--delete --no-follow-symlinks --exclude "*" --include "*.zip"

aws cloudfront create-invalidation \
--distribution-id EKT7LT5PM8RKS \
--paths "${PATH_PREFIX}" "${PATH_PREFIX}/*"
Expand All @@ -377,8 +403,8 @@ jobs:
# Don't block the preview deploy on it; treat a permissions gap as a follow-up, not an outage.
continue-on-error: true
run: |
if [ -f .artifacts/docs/html/redirects.json ]; then
dotnet run --project src/tooling/docs-builder -- assembler deploy update-redirects --no-delete preview --redirectsFile .artifacts/docs/html/redirects.json
if [ -f /tmp/redirects.json ]; then
dotnet run --no-build -c release --project src/tooling/docs-builder -- assembler deploy update-redirects --no-delete preview --redirectsFile /tmp/redirects.json
fi

- name: Update Link Index
Expand All @@ -390,6 +416,8 @@ jobs:
&& steps.internal-validate-path-prefixes.outcome == 'success'
)
uses: elastic/docs-builder/actions/update-link-index@main
with:
link_reference_file: /tmp/links.json

- name: Update deployment status
uses: actions/github-script@v9
Expand Down
7 changes: 0 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -303,13 +303,6 @@ jobs:
with:
ref: ${{ needs.release-drafter.outputs.tag_name }}
persist-credentials: false
- name: 'Windows only, set TEMP to the same drive'
if: ${{ matrix.os == 'windows-latest' }}
# temporary waiting for https://github.com/parcel-bundler/parcel/pull/10095 to fix
run: |
mkdir "D:\\Temp"
echo "TEMP=D:\\Temp" >> $env:GITHUB_ENV

- name: Bootstrap Action Workspace
id: bootstrap
uses: ./.github/actions/bootstrap
Expand Down
3 changes: 3 additions & 0 deletions src/Elastic.Documentation.Site/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
.parcel-cache/
node_modules/
_static/assets/
_static/asset-*
_static/chunk-*
_static/*.css
_static/*.css.map
_static/*.ttf
Expand Down
6 changes: 0 additions & 6 deletions src/Elastic.Documentation.Site/.parcelrc

This file was deleted.

6 changes: 0 additions & 6 deletions src/Elastic.Documentation.Site/.postcssrc

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,7 @@ function registerFetchInstrumentation(): void {
propagateTraceHeaderCorsUrls: [
new RegExp(`${window.location.origin}/.*`),
],
ignoreUrls: [
/_api\/v1\/o\/.*/,
/_api\/v1\/?$/,
/__parcel_code_frame$/,
],
ignoreUrls: [/_api\/v1\/o\/.*/, /_api\/v1\/?$/],
}),
],
})
Expand Down
67 changes: 40 additions & 27 deletions src/Elastic.Documentation.Site/Elastic.Documentation.Site.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated</InterceptorsPreviewNamespaces>
<IsPublishable>true</IsPublishable>
<EnableDefaultRazorSlices>false</EnableDefaultRazorSlices>
<SkipNpmBuild Condition="'$(SkipNpmBuild)' == ''">false</SkipNpmBuild>
</PropertyGroup>

<ItemGroup>
Expand All @@ -35,7 +36,17 @@
The first time the task is executed. Then, it only runs when you change the package.json file.
Documentation: https://learn.microsoft.com/en-us/visualstudio/msbuild/incremental-builds?WT.mc_id=DT-MVP-5003978
-->
<Target Name="NpmInstall" Inputs="package.json" Outputs="node_modules/.install-stamp">
<Target Name="EnsureSkipNpmBuildArtifacts"
BeforeTargets="NpmInstall;NpmRunBuild"
Condition="'$(SkipNpmBuild)' == 'true'">
<Error Condition="!Exists('_static/main.js') Or !Exists('_static/styles.css') Or !Exists('_static/assembler.css') Or !Exists('_static/isolated.css') Or !Exists('_static/codex.css')"

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.

[MEDIUM] SkipNpmBuild check still allows incomplete Vite output

EnsureSkipNpmBuildArtifacts verifies only five top-level files, but Vite runtime also needs generated chunk-*/asset-* files referenced by main.js. In SkipNpmBuild=true builds, a partially-populated _static/ directory can pass this check and produce runtime 404s when the browser requests missing chunks/assets.

Please also validate bundle completeness for the skip path (for example by checking for required generated chunk/asset outputs or validating a manifest from the last full build).

Text="SkipNpmBuild is set but one or more _static artifacts are missing. Run a full frontend build first." />
</Target>

<Target Name="NpmInstall"
Condition="'$(DesignTimeBuild)' != 'true' And '$(SkipNpmBuild)' != 'true'"
Inputs="package.json;package-lock.json"
Outputs="node_modules/.install-stamp">
<Exec Command="npm ci" WorkingDirectory="$(MSBuildThisFileDirectory)" ConsoleToMsBuild="true" />

<!-- Write the stamp file, so incremental builds work -->
Expand All @@ -57,8 +68,7 @@
<NpmBuildInputFiles Include="package-lock.json" />
<NpmBuildInputFiles Include="tsconfig.json" />
<NpmBuildInputFiles Include="tailwind.config.js" />
<NpmBuildInputFiles Include=".parcelrc" />
<NpmBuildInputFiles Include=".postcssrc" />
<NpmBuildInputFiles Include="vite.config.ts" />
</ItemGroup>
<!-- Tailwind scans these directories for utility class usage (see tailwind.config.js content paths).
Resolved inside a target so external cshtml files don't appear in the IDE's project tree. -->
Expand All @@ -73,44 +83,47 @@
<Target Name="NpmRunBuild"
DependsOnTargets="NpmInstall;MinVer;CollectExternalCshtmlInputs"
BeforeTargets="BeforeBuild"
Condition="'$(DesignTimeBuild)' != 'true' And '$(SkipNpmBuild)' != 'true'"
Inputs="@(NpmBuildInputFiles)"
Outputs="_static/.build-stamp;_static/main.js;_static/styles.css">
<Message Text="Building frontend with version: $(MinVerVersion)" Importance="high" />
<Exec Command="npm run build"
WorkingDirectory="$(MSBuildThisFileDirectory)"
ConsoleToMsBuild="true"
EnvironmentVariables="DOCS_BUILDER_VERSION=$(MinVerVersion)">
<Output TaskParameter="ConsoleOutput" PropertyName="OutputOfExec" />
</Exec>
EnvironmentVariables="DOCS_BUILDER_VERSION=$(MinVerVersion)"
IgnoreStandardErrorWarningFormat="true" />
<!-- Write the stamp file, so incremental builds work -->
<Touch Files="_static/.build-stamp" AlwaysCreate="true" />
</Target>

<!-- Clean old hashed assets before each frontend build to avoid accumulating stale files.
Without this, multiple Parcel runs (e.g. across dotnet build vs dotnet run) can leave
old hashed filenames in _static; all get embedded and then copied to assembler output,
causing duplicated JS/CSS with different hashes in the final _static folder. -->
<Target Name="CleanOldHashedAssets" BeforeTargets="NpmRunBuild" Condition="'$(IsPublishing)' == 'true'">
<Target Name="CleanGeneratedStaticAssets"
BeforeTargets="NpmRunBuild"

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] Cleanup runs even when frontend build is skipped, leaving broken chunk references

CleanGeneratedStaticAssets runs BeforeTargets="NpmRunBuild" and deletes chunk-*/asset-* files regardless of whether NpmRunBuild actually executes. Because NpmRunBuild can be skipped by incremental checks, this can leave _static/main.js referencing chunk files that were just deleted.

I reproduced this on the current branch by invoking NpmRunBuild via MSBuild and then checking _static/main.js: it contains 45 ./chunk-* imports, and all 45 referenced files were missing on disk after the target graph ran.

Please make cleanup conditional on the build actually running (for example, move cleanup into NpmRunBuild before the Exec), so no-op builds don't invalidate an otherwise consistent _static output set.

Condition="'$(DesignTimeBuild)' != 'true' And '$(SkipNpmBuild)' != 'true'">
<RemoveDir Directories="_static/assets" Condition="Exists('_static/assets')" />
<ItemGroup>
<OldHashedFiles Include="_static/*.????????*.js" />
<OldHashedFiles Include="_static/*.????????*.css" />
<OldHashedFiles Include="_static/*.????????*.js.map" />
<OldHashedFiles Include="_static/*.????????*.css.map" />
<!-- Only remove content-hashed chunk/asset files from prior Vite runs so they
don't accumulate across builds. Do NOT list the stable named outputs here
(main.js, styles.css, etc.) — deleting them before NpmRunBuild's incremental
output check would force a full npm run build on every dotnet build invocation
even when no source files changed. -->
<ViteGeneratedFiles Include="_static/asset-*" />
<ViteGeneratedFiles Include="_static/chunk-*" />
<OldHashedBundles Include="_static/*.????????*.js" />
<OldHashedBundles Include="_static/*.????????*.css" />
</ItemGroup>
<Delete Files="@(OldHashedFiles)" ContinueOnError="true" />
<Delete Files="@(ViteGeneratedFiles);@(OldHashedBundles)" ContinueOnError="true" />
</Target>

<Target Name="EmbedGeneratedAssets" AfterTargets="NpmRunBuild">
<Target Name="EmbedGeneratedAssets" DependsOnTargets="NpmRunBuild" BeforeTargets="BeforeBuild">
<ItemGroup>
<EmbeddedResource Include="_static/*.js" Watch="false" />
<EmbeddedResource Include="_static/*.js.map" Watch="false" Condition="'$(Configuration)' == 'Debug'" />
<EmbeddedResource Include="_static/*.css" Watch="false" />
<EmbeddedResource Include="_static/*.css.map" Watch="false" Condition="'$(Configuration)' == 'Debug'" />
<EmbeddedResource Include="_static/*.svg" Watch="false" />
<EmbeddedResource Include="_static/*.png" Watch="false" />
<EmbeddedResource Include="_static/*.ttf" Watch="false" />
<EmbeddedResource Include="_static/*.woff" Watch="false" />
<EmbeddedResource Include="_static/*.woff2" Watch="false" />
<EmbeddedResource Include="_static/**/*.js" Watch="false" />
<EmbeddedResource Include="_static/**/*.js.map" Watch="false" Condition="'$(Configuration)' == 'Debug'" />
<EmbeddedResource Include="_static/**/*.css" Watch="false" />
<EmbeddedResource Include="_static/**/*.css.map" Watch="false" Condition="'$(Configuration)' == 'Debug'" />
<EmbeddedResource Include="_static/**/*.svg" Watch="false" />
<EmbeddedResource Include="_static/**/*.png" Watch="false" />
<EmbeddedResource Include="_static/**/*.ttf" Watch="false" />
<EmbeddedResource Include="_static/**/*.woff" Watch="false" />
<EmbeddedResource Include="_static/**/*.woff2" Watch="false" />
</ItemGroup>
</Target>

Expand Down
2 changes: 1 addition & 1 deletion src/Elastic.Documentation.Site/Layout/_Head.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
@* hx-history=false stops new snapshots but still restores sessionStorage
leftovers from older pages, which keeps the previous island tree. *@
<script>try{sessionStorage.removeItem("htmx-history-cache")}catch(e){}</script>
<script src="@Model.Static("main.js")" defer></script>
<script type="module" src="@Model.Static("main.js")"></script>
@if (Model.BuildType == BuildType.Assembler && !Model.Features.AirGappedEnabled)
{
@* In <head> so head-support's merge keeps the identical tag across htmx navigations
Expand Down
Loading
Loading