Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
15e6ee5
Allow limited public assembler builds on docs-builder
cotti Dec 17, 2025
fc05e7e
Use path prefix for llms.txt when available
cotti Dec 17, 2025
3b228a0
Adjust link-index.snapshot generation with path prefixes
cotti Dec 17, 2025
4ca1805
Adjust sitemap.xml generation to use path prefixes when available.
cotti Dec 17, 2025
ed133bf
Remove extra slash
cotti Dec 17, 2025
71dbdaf
Create assembler deployment process
cotti Dec 17, 2025
ebf0abb
Apply automated fix to 'Call to System.IO.Path.Combine'
cotti Dec 17, 2025
31f208e
Apply remaining updates
cotti Dec 17, 2025
f46f699
Move the assembler preview to a separate workflow
cotti Dec 18, 2025
76643cb
Merge branch 'main' into feature/db-assembler-previews
cotti Dec 18, 2025
9fed4a8
Revert "Move the assembler preview to a separate workflow"
cotti Dec 18, 2025
2fd9d4f
Revert "Create assembler deployment process"
cotti Dec 18, 2025
baf4c87
Revert "Remove extra slash"
cotti Dec 18, 2025
1b3dcaf
Revert "Allow limited public assembler builds on docs-builder"
cotti Dec 18, 2025
7d6b125
Merge remote-tracking branch 'origin/feature/db-assembler-previews' i…
cotti Dec 18, 2025
38a1160
Add workflow
cotti Dec 22, 2025
11a183a
Remove label execution
cotti Dec 22, 2025
70d1290
Merge branch 'main' into feature/db-assembler-previews
cotti Dec 22, 2025
eb54ed8
Add labeled clause
cotti Dec 22, 2025
13479f0
Merge remote-tracking branch 'origin/feature/db-assembler-previews' i…
cotti Dec 22, 2025
d43f146
Fix SHA evalutation on PRs
cotti Dec 22, 2025
929efdd
Un-revert assembler preview config
cotti Dec 22, 2025
a81cde5
Simplify usage of path-prefixed output
cotti Dec 22, 2025
c82d89c
Add suggested change to 'Call to System.IO.Path.Combine'
cotti Dec 22, 2025
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
154 changes: 154 additions & 0 deletions .github/workflows/assembler-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
name: assembler-preview

on:
pull_request:
types:
- opened
- synchronize
- reopened
- labeled
workflow_dispatch:
inputs:
pr_number:
description: 'Pull Request number to build the assembler preview for'
required: true
type: string

permissions:
contents: read
deployments: write
id-token: write
pull-requests: read

concurrency:
group: assembler-preview-${{ github.event.pull_request.number || inputs.pr_number }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
env:
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
steps:
- name: Get PR details
if: github.event_name == 'workflow_dispatch'
id: pr-details
uses: actions/github-script@v8
env:
PR_NUMBER: ${{ inputs.pr_number }}
with:
result-encoding: json
script: |
const { owner, repo } = context.repo;
const prNumber = parseInt(process.env.PR_NUMBER, 10);

if (isNaN(prNumber) || prNumber <= 0) {
core.setFailed(`Invalid PR number: ${process.env.PR_NUMBER}`);
return;
}

try {
const { data: pr } = await github.rest.pulls.get({
owner,
repo,
pull_number: prNumber,
});

return {
sha: pr.head.sha,
ref: pr.head.ref,
base_ref: pr.base.ref,
};
} catch (error) {
core.setFailed(`Failed to get PR #${prNumber}: ${error.message}`);
}

- name: Set PR SHA (workflow_dispatch)
id: pr-sha-dispatch
if: github.event_name == 'workflow_dispatch'
run: echo "sha=${{ fromJSON(steps.pr-details.outputs.result).sha }}" >> $GITHUB_OUTPUT

- name: Set PR SHA (pull_request)
id: pr-sha-pr
if: github.event_name == 'pull_request'
run: echo "sha=${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT

- name: Resolve PR SHA
id: pr-sha
run: echo "sha=${{ steps.pr-sha-dispatch.outputs.sha || steps.pr-sha-pr.outputs.sha }}" >> $GITHUB_OUTPUT

- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ steps.pr-sha.outputs.sha }}
persist-credentials: false

- name: Create Deployment
uses: actions/github-script@v8
id: deployment
env:
PR_SHA: ${{ steps.pr-sha.outputs.sha }}
with:
result-encoding: string
script: |
const { owner, repo } = context.repo;
const prNumber = process.env.PR_NUMBER;
const environment = 'assembler-preview';
const task = `assembler-preview-${prNumber}`;
const deployment = await github.rest.repos.createDeployment({
owner,
repo,
environment,
task,
ref: process.env.PR_SHA,
auto_merge: false,
transient_environment: true,
required_contexts: [],
})
await github.rest.repos.createDeploymentStatus({
deployment_id: deployment.data.id,
owner,
repo,
state: "in_progress",
log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
})
return deployment.data.id

- name: Bootstrap Action Workspace
uses: elastic/docs-builder/.github/actions/bootstrap@main

- name: Build assembled documentation
id: assembler-build
env:
ASSEMBLER_PREVIEW_PATH_PREFIX: ${{ github.repository }}/docs/${{ env.PR_NUMBER }}
run: |
echo "ASSEMBLER_PREVIEW_PATH_PREFIX=${ASSEMBLER_PREVIEW_PATH_PREFIX}" >> $GITHUB_ENV
yq -i ".environments.preview.path_prefix = \"${ASSEMBLER_PREVIEW_PATH_PREFIX}\"" config/assembler.yml
dotnet run --project src/tooling/docs-builder -- assemble --skip-private-repositories --environment preview

- uses: elastic/docs-builder/.github/actions/aws-auth@main

- name: Upload assembled docs to S3
id: s3-upload
env:
AWS_RETRY_MODE: standard
AWS_MAX_ATTEMPTS: 6
run: |
aws s3 sync .artifacts/assembly/${ASSEMBLER_PREVIEW_PATH_PREFIX} "s3://elastic-docs-v3-website-preview/${ASSEMBLER_PREVIEW_PATH_PREFIX}" --delete --no-follow-symlinks
aws cloudfront create-invalidation \
--distribution-id EKT7LT5PM8RKS \
--paths "/${ASSEMBLER_PREVIEW_PATH_PREFIX}" "/${ASSEMBLER_PREVIEW_PATH_PREFIX}/*"

- name: Update Deployment Status
if: always() && steps.deployment.outputs.result
uses: actions/github-script@v8
with:
script: |
await github.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: ${{ steps.deployment.outputs.result }},
state: "${{ steps.s3-upload.outcome == 'success' && 'success' || 'failure' }}",
environment_url: `https://docs-v3-preview.elastic.dev/${process.env.ASSEMBLER_PREVIEW_PATH_PREFIX}`,
log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
})
8 changes: 8 additions & 0 deletions config/assembler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ environments:
path_prefix: docs
feature_flags:
SEARCH_OR_ASK_AI: true
preview:
uri: https://docs-v3-preview.elastic.dev
path_prefix: ${ASSEMBLER_PREVIEW_PATH_PREFIX}
content_source: current
google_tag_manager:
enabled: false
feature_flags:
SEARCH_OR_ASK_AI: true

shared_configuration:
stack: &stack
Expand Down
2 changes: 1 addition & 1 deletion src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class LlmMarkdownExporter : IMarkdownExporter

public async ValueTask<bool> FinishExportAsync(IDirectoryInfo outputFolder, Cancel ctx)
{
var outputDirectory = Path.Combine(outputFolder.FullName, "docs");
var outputDirectory = outputFolder.FullName;
Copy link
Member

Choose a reason for hiding this comment

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

We expose llm.zip on elastic.co so I think this needs to be put under docs again.

var zipPath = Path.Combine(outputDirectory, "llm.zip");

// Create the llms.txt file with boilerplate content
Expand Down
12 changes: 12 additions & 0 deletions src/services/Elastic.Documentation.Assembler/AssembleContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public class AssembleContext : IDocumentationConfigurationContext

public IDirectoryInfo OutputDirectory { get; }

/// <summary>
/// The output directory with the path prefix applied.
/// This is where assembled content (sitemap.xml, llms.txt, link-index.snapshot.json, etc.) should be written.
/// </summary>
public IDirectoryInfo OutputWithPathPrefixDirectory { get; }

/// <inheritdoc />
public IFileInfo ConfigurationPath { get; }

Expand Down Expand Up @@ -83,5 +89,11 @@ public AssembleContext(
CheckoutDirectory = ReadFileSystem.DirectoryInfo.New(checkoutDirectory ?? defaultCheckoutDirectory);
var defaultOutputDirectory = Path.Combine(Paths.WorkingDirectoryRoot.FullName, ".artifacts", "assembly");
OutputDirectory = ReadFileSystem.DirectoryInfo.New(output ?? defaultOutputDirectory);

// Calculate the output directory with path prefix once
var pathPrefix = Environment.PathPrefix;
OutputWithPathPrefixDirectory = string.IsNullOrEmpty(pathPrefix)
? OutputDirectory
: WriteFileSystem.DirectoryInfo.New(WriteFileSystem.Path.Combine(OutputDirectory.FullName, pathPrefix));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Cancel ctx

if (exporters.Contains(Exporter.Html))
{
var sitemapBuilder = new SitemapBuilder(navigation.NavigationItems, assembleContext.WriteFileSystem, assembleContext.OutputDirectory);
var sitemapBuilder = new SitemapBuilder(navigation.NavigationItems, assembleContext.WriteFileSystem, assembleContext.OutputWithPathPrefixDirectory);
sitemapBuilder.Generate();
}

Expand All @@ -140,21 +140,20 @@ Cancel ctx

private static async Task EnhanceLlmsTxtFile(AssembleContext context, SiteNavigation navigation, LlmsNavigationEnhancer enhancer, Cancel ctx)
{
var llmsTxtPath = Path.Combine(context.OutputDirectory.FullName, "docs", "llms.txt");
var pathPrefixedOutputFolder = context.OutputWithPathPrefixDirectory;
var llmsTxtPath = context.ReadFileSystem.Path.Combine(pathPrefixedOutputFolder.FullName, "llms.txt");

var readFs = context.ReadFileSystem;
if (!readFs.File.Exists(llmsTxtPath))
if (!context.ReadFileSystem.File.Exists(llmsTxtPath))
return; // No llms.txt file to enhance

var existingContent = await readFs.File.ReadAllTextAsync(llmsTxtPath, ctx);
var existingContent = await context.ReadFileSystem.File.ReadAllTextAsync(llmsTxtPath, ctx);
// Assembler always uses the production URL as canonical base URL
var canonicalBaseUrl = new Uri(context.Environment.Uri);
var navigationSections = enhancer.GenerateNavigationSections(navigation, canonicalBaseUrl);

// Append the navigation sections to the existing boilerplate
var enhancedContent = existingContent + Environment.NewLine + navigationSections;

var writeFs = context.WriteFileSystem;
await writeFs.File.WriteAllTextAsync(llmsTxtPath, enhancedContent, Encoding.UTF8, ctx);
await context.WriteFileSystem.File.WriteAllTextAsync(llmsTxtPath, enhancedContent, Encoding.UTF8, ctx);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public async Task BuildAllAsync(PublishEnvironment environment, FrozenDictionary
foreach (var exporter in markdownExporters)
{
_logger.LogInformation("Calling FinishExportAsync on {ExporterName}", exporter.GetType().Name);
_ = await exporter.FinishExportAsync(context.OutputDirectory, ctx);
_ = await exporter.FinishExportAsync(context.OutputWithPathPrefixDirectory, ctx);
}

if (exportOptions.Contains(Exporter.Redirects))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Elastic.Documentation.Assembler.Building;
public class SitemapBuilder(
IReadOnlyCollection<INavigationItem> navigationItems,
IFileSystem fileSystem,
IDirectoryInfo outputFolder
IDirectoryInfo pathPrefixedOutputFolder
)
{
private static readonly Uri BaseUri = new("https://www.elastic.co");
Expand Down Expand Up @@ -54,7 +54,7 @@ public void Generate()

doc.Add(root);

using var fileStream = fileSystem.File.Create(Path.Combine(outputFolder.ToString() ?? string.Empty, "docs", "sitemap.xml"));
using var fileStream = fileSystem.File.Create(fileSystem.Path.Combine(pathPrefixedOutputFolder.FullName, "sitemap.xml"));
doc.Save(fileStream);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ await context.WriteFileSystem.File.WriteAllTextAsync(
}

public async Task WriteLinkRegistrySnapshot(LinkRegistry linkRegistrySnapshot, Cancel ctx = default) => await context.WriteFileSystem.File.WriteAllTextAsync(
Path.Combine(context.OutputDirectory.FullName, "docs", CheckoutResult.LinkRegistrySnapshotFileName),
context.WriteFileSystem.Path.Combine(context.OutputWithPathPrefixDirectory.FullName, CheckoutResult.LinkRegistrySnapshotFileName),
LinkRegistry.Serialize(linkRegistrySnapshot),
ctx
);
Expand Down
Loading