Skip to content

Commit d08d831

Browse files
Allow limited public assembler builds on docs-builder (#2379)
* Allow limited public assembler builds on docs-builder * Use path prefix for llms.txt when available * Adjust link-index.snapshot generation with path prefixes * Adjust sitemap.xml generation to use path prefixes when available. * Remove extra slash * Create assembler deployment process * Apply automated fix to 'Call to System.IO.Path.Combine' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * Apply remaining updates * Move the assembler preview to a separate workflow * Revert "Move the assembler preview to a separate workflow" This reverts commit f46f699. * Revert "Create assembler deployment process" This reverts commit 71dbdaf. * Revert "Remove extra slash" This reverts commit ed133bf. * Revert "Allow limited public assembler builds on docs-builder" This reverts commit 15e6ee5. * Add workflow * Remove label execution * Add labeled clause * Fix SHA evalutation on PRs * Un-revert assembler preview config * Simplify usage of path-prefixed output * Add suggested change to 'Call to System.IO.Path.Combine' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * Re-consolidate PR Details step, set path_prefix at job level * Add cleanup workflow --------- Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
1 parent b6272c7 commit d08d831

File tree

9 files changed

+219
-12
lines changed

9 files changed

+219
-12
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: assembler-preview-cleanup
2+
3+
on:
4+
workflow_call: ~
5+
pull_request_target:
6+
types: [closed]
7+
8+
permissions:
9+
contents: none
10+
deployments: write
11+
id-token: write
12+
13+
jobs:
14+
destroy:
15+
if: github.event.repository.fork == false # Skip running the job on the fork itself (It still runs on PRs on the upstream from forks)
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Delete GitHub environment
19+
uses: actions/github-script@v8
20+
id: delete-deployment
21+
with:
22+
script: |
23+
const { owner, repo } = context.repo;
24+
const deployments = await github.rest.repos.listDeployments({
25+
owner,
26+
repo,
27+
environment: 'assembler-preview',
28+
task: `assembler-preview-${context.issue.number}`,
29+
});
30+
core.setOutput('is-empty', deployments.data.length === 0)
31+
for (const deployment of deployments.data) {
32+
await github.rest.repos.createDeploymentStatus({
33+
owner,
34+
repo,
35+
deployment_id: deployment.id,
36+
state: 'inactive',
37+
description: 'Marking deployment as inactive'
38+
});
39+
await github.rest.repos.deleteDeployment({
40+
owner,
41+
repo,
42+
deployment_id: deployment.id
43+
});
44+
}
45+
46+
- uses: elastic/docs-builder/.github/actions/aws-auth@main
47+
if: steps.delete-deployment.outputs.is-empty == 'false'
48+
49+
- name: Delete s3 objects
50+
if: steps.delete-deployment.outputs.is-empty == 'false'
51+
env:
52+
PR_NUMBER: ${{ github.event.pull_request.number }}
53+
run: |
54+
aws s3 rm "s3://elastic-docs-v3-website-preview/${GITHUB_REPOSITORY}/docs/${PR_NUMBER}" --recursive
55+
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
name: assembler-preview
2+
3+
on:
4+
pull_request:
5+
types:
6+
- opened
7+
- synchronize
8+
- reopened
9+
- labeled
10+
workflow_dispatch:
11+
inputs:
12+
pr_number:
13+
description: 'Pull Request number to build the assembler preview for'
14+
required: true
15+
type: string
16+
17+
permissions:
18+
contents: read
19+
deployments: write
20+
id-token: write
21+
pull-requests: read
22+
23+
concurrency:
24+
group: assembler-preview-${{ github.event.pull_request.number || inputs.pr_number }}
25+
cancel-in-progress: true
26+
27+
jobs:
28+
build:
29+
runs-on: ubuntu-latest
30+
env:
31+
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
32+
ASSEMBLER_PREVIEW_PATH_PREFIX: ${{ github.repository }}/docs/${{ github.event.pull_request.number || inputs.pr_number }}
33+
steps:
34+
- name: Get PR details
35+
if: github.event_name == 'workflow_dispatch'
36+
id: pr-details
37+
uses: actions/github-script@v8
38+
env:
39+
PR_NUMBER: ${{ inputs.pr_number }}
40+
with:
41+
result-encoding: string
42+
script: |
43+
const { owner, repo } = context.repo;
44+
const prNumber = parseInt(process.env.PR_NUMBER, 10);
45+
46+
if (isNaN(prNumber) || prNumber <= 0) {
47+
core.setFailed(`Invalid PR number: ${process.env.PR_NUMBER}`);
48+
return;
49+
}
50+
51+
try {
52+
const { data: pr } = await github.rest.pulls.get({
53+
owner,
54+
repo,
55+
pull_number: prNumber,
56+
});
57+
58+
return pr.head.sha;
59+
} catch (error) {
60+
core.setFailed(`Failed to get PR #${prNumber}: ${error.message}`);
61+
}
62+
63+
- name: Checkout
64+
uses: actions/checkout@v6
65+
with:
66+
ref: ${{ steps.pr-details.outputs.result || github.event.pull_request.head.sha }}
67+
persist-credentials: false
68+
69+
- name: Create Deployment
70+
uses: actions/github-script@v8
71+
id: deployment
72+
env:
73+
PR_SHA: ${{ steps.pr-details.outputs.result || github.event.pull_request.head.sha }}
74+
with:
75+
result-encoding: string
76+
script: |
77+
const { owner, repo } = context.repo;
78+
const prNumber = process.env.PR_NUMBER;
79+
const environment = 'assembler-preview';
80+
const task = `assembler-preview-${prNumber}`;
81+
const deployment = await github.rest.repos.createDeployment({
82+
owner,
83+
repo,
84+
environment,
85+
task,
86+
ref: process.env.PR_SHA,
87+
auto_merge: false,
88+
transient_environment: true,
89+
required_contexts: [],
90+
})
91+
await github.rest.repos.createDeploymentStatus({
92+
deployment_id: deployment.data.id,
93+
owner,
94+
repo,
95+
state: "in_progress",
96+
log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
97+
})
98+
return deployment.data.id
99+
100+
- name: Bootstrap Action Workspace
101+
uses: elastic/docs-builder/.github/actions/bootstrap@main
102+
103+
- name: Build assembled documentation
104+
run: |
105+
yq -i ".environments.preview.path_prefix = \"${ASSEMBLER_PREVIEW_PATH_PREFIX}\"" config/assembler.yml
106+
dotnet run --project src/tooling/docs-builder -- assemble --skip-private-repositories --environment preview
107+
108+
- uses: elastic/docs-builder/.github/actions/aws-auth@main
109+
110+
- name: Upload assembled docs to S3
111+
id: s3-upload
112+
env:
113+
AWS_RETRY_MODE: standard
114+
AWS_MAX_ATTEMPTS: 6
115+
run: |
116+
aws s3 sync .artifacts/assembly/${ASSEMBLER_PREVIEW_PATH_PREFIX} "s3://elastic-docs-v3-website-preview/${ASSEMBLER_PREVIEW_PATH_PREFIX}" --delete --no-follow-symlinks
117+
aws cloudfront create-invalidation \
118+
--distribution-id EKT7LT5PM8RKS \
119+
--paths "/${ASSEMBLER_PREVIEW_PATH_PREFIX}" "/${ASSEMBLER_PREVIEW_PATH_PREFIX}/*"
120+
121+
- name: Update Deployment Status
122+
if: always() && steps.deployment.outputs.result
123+
uses: actions/github-script@v8
124+
with:
125+
script: |
126+
await github.rest.repos.createDeploymentStatus({
127+
owner: context.repo.owner,
128+
repo: context.repo.repo,
129+
deployment_id: ${{ steps.deployment.outputs.result }},
130+
state: "${{ steps.s3-upload.outcome == 'success' && 'success' || 'failure' }}",
131+
environment_url: `https://docs-v3-preview.elastic.dev/${process.env.ASSEMBLER_PREVIEW_PATH_PREFIX}`,
132+
log_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
133+
})

config/assembler.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ environments:
3636
path_prefix: docs
3737
feature_flags:
3838
SEARCH_OR_ASK_AI: true
39+
preview:
40+
uri: https://docs-v3-preview.elastic.dev
41+
path_prefix: ${ASSEMBLER_PREVIEW_PATH_PREFIX}
42+
content_source: current
43+
google_tag_manager:
44+
enabled: false
45+
feature_flags:
46+
SEARCH_OR_ASK_AI: true
3947

4048
shared_configuration:
4149
stack: &stack

src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public class LlmMarkdownExporter : IMarkdownExporter
4848

4949
public async ValueTask<bool> FinishExportAsync(IDirectoryInfo outputFolder, Cancel ctx)
5050
{
51-
var outputDirectory = Path.Combine(outputFolder.FullName, "docs");
51+
var outputDirectory = outputFolder.FullName;
5252
var zipPath = Path.Combine(outputDirectory, "llm.zip");
5353

5454
// Create the llms.txt file with boilerplate content

src/services/Elastic.Documentation.Assembler/AssembleContext.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ public class AssembleContext : IDocumentationConfigurationContext
4242

4343
public IDirectoryInfo OutputDirectory { get; }
4444

45+
/// <summary>
46+
/// The output directory with the path prefix applied.
47+
/// This is where assembled content (sitemap.xml, llms.txt, link-index.snapshot.json, etc.) should be written.
48+
/// </summary>
49+
public IDirectoryInfo OutputWithPathPrefixDirectory { get; }
50+
4551
/// <inheritdoc />
4652
public IFileInfo ConfigurationPath { get; }
4753

@@ -83,5 +89,11 @@ public AssembleContext(
8389
CheckoutDirectory = ReadFileSystem.DirectoryInfo.New(checkoutDirectory ?? defaultCheckoutDirectory);
8490
var defaultOutputDirectory = Path.Combine(Paths.WorkingDirectoryRoot.FullName, ".artifacts", "assembly");
8591
OutputDirectory = ReadFileSystem.DirectoryInfo.New(output ?? defaultOutputDirectory);
92+
93+
// Calculate the output directory with path prefix once
94+
var pathPrefix = Environment.PathPrefix;
95+
OutputWithPathPrefixDirectory = string.IsNullOrEmpty(pathPrefix)
96+
? OutputDirectory
97+
: WriteFileSystem.DirectoryInfo.New(WriteFileSystem.Path.Combine(OutputDirectory.FullName, pathPrefix));
8698
}
8799
}

src/services/Elastic.Documentation.Assembler/Building/AssemblerBuildService.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ Cancel ctx
120120

121121
if (exporters.Contains(Exporter.Html))
122122
{
123-
var sitemapBuilder = new SitemapBuilder(navigation.NavigationItems, assembleContext.WriteFileSystem, assembleContext.OutputDirectory);
123+
var sitemapBuilder = new SitemapBuilder(navigation.NavigationItems, assembleContext.WriteFileSystem, assembleContext.OutputWithPathPrefixDirectory);
124124
sitemapBuilder.Generate();
125125
}
126126

@@ -140,21 +140,20 @@ Cancel ctx
140140

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

145-
var readFs = context.ReadFileSystem;
146-
if (!readFs.File.Exists(llmsTxtPath))
146+
if (!context.ReadFileSystem.File.Exists(llmsTxtPath))
147147
return; // No llms.txt file to enhance
148148

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

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

157-
var writeFs = context.WriteFileSystem;
158-
await writeFs.File.WriteAllTextAsync(llmsTxtPath, enhancedContent, Encoding.UTF8, ctx);
157+
await context.WriteFileSystem.File.WriteAllTextAsync(llmsTxtPath, enhancedContent, Encoding.UTF8, ctx);
159158
}
160159
}

src/services/Elastic.Documentation.Assembler/Building/AssemblerBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public async Task BuildAllAsync(PublishEnvironment environment, FrozenDictionary
7575
foreach (var exporter in markdownExporters)
7676
{
7777
_logger.LogInformation("Calling FinishExportAsync on {ExporterName}", exporter.GetType().Name);
78-
_ = await exporter.FinishExportAsync(context.OutputDirectory, ctx);
78+
_ = await exporter.FinishExportAsync(context.OutputWithPathPrefixDirectory, ctx);
7979
}
8080

8181
if (exportOptions.Contains(Exporter.Redirects))

src/services/Elastic.Documentation.Assembler/Building/SitemapBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace Elastic.Documentation.Assembler.Building;
1616
public class SitemapBuilder(
1717
IReadOnlyCollection<INavigationItem> navigationItems,
1818
IFileSystem fileSystem,
19-
IDirectoryInfo outputFolder
19+
IDirectoryInfo pathPrefixedOutputFolder
2020
)
2121
{
2222
private static readonly Uri BaseUri = new("https://www.elastic.co");
@@ -54,7 +54,7 @@ public void Generate()
5454

5555
doc.Add(root);
5656

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

src/services/Elastic.Documentation.Assembler/Sourcing/RepositorySourcesFetcher.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ await context.WriteFileSystem.File.WriteAllTextAsync(
118118
}
119119

120120
public async Task WriteLinkRegistrySnapshot(LinkRegistry linkRegistrySnapshot, Cancel ctx = default) => await context.WriteFileSystem.File.WriteAllTextAsync(
121-
Path.Combine(context.OutputDirectory.FullName, "docs", CheckoutResult.LinkRegistrySnapshotFileName),
121+
context.WriteFileSystem.Path.Combine(context.OutputWithPathPrefixDirectory.FullName, CheckoutResult.LinkRegistrySnapshotFileName),
122122
LinkRegistry.Serialize(linkRegistrySnapshot),
123123
ctx
124124
);

0 commit comments

Comments
 (0)