Skip to content

Commit 7d4ff28

Browse files
committed
Extract developer guide report summaries to script
1 parent e9ae618 commit 7d4ff28

File tree

6 files changed

+435
-8
lines changed

6 files changed

+435
-8
lines changed

.github/workflows/developer-guide-docs.yml

Lines changed: 182 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,43 @@ jobs:
8282
with:
8383
ruby-version: '3.1'
8484

85+
- name: Set up Java for docToolchain
86+
uses: actions/setup-java@v4
87+
with:
88+
distribution: temurin
89+
java-version: '21'
90+
8591
- name: Install Asciidoctor tooling
8692
run: |
8793
gem install --no-document asciidoctor asciidoctor-pdf
8894
95+
- name: Download docToolchain
96+
run: |
97+
set -euo pipefail
98+
DT_VERSION="3.4.2"
99+
DT_ROOT="${RUNNER_TEMP}/doctoolchain"
100+
mkdir -p "$DT_ROOT"
101+
curl -fsSL -o "$DT_ROOT/doctoolchain.zip" "https://github.com/docToolchain/docToolchain/releases/download/v${DT_VERSION}/docToolchain-${DT_VERSION}.zip"
102+
unzip -q "$DT_ROOT/doctoolchain.zip" -d "$DT_ROOT"
103+
DOC_TOOLCHAIN_HOME="$DT_ROOT/docToolchain-${DT_VERSION}"
104+
echo "DOC_TOOLCHAIN_HOME=$DOC_TOOLCHAIN_HOME" >> "$GITHUB_ENV"
105+
106+
- name: Run docToolchain AsciiDoc linter
107+
run: |
108+
set -euo pipefail
109+
REPORT_DIR="build/developer-guide/reports"
110+
REPORT_FILE="${REPORT_DIR}/asciidoc-lint-report.txt"
111+
mkdir -p "$REPORT_DIR"
112+
set +e
113+
"$DOC_TOOLCHAIN_HOME/bin/doctoolchain" docs/developer-guide asciidocLint | tee "$REPORT_FILE"
114+
STATUS=${PIPESTATUS[0]}
115+
set -e
116+
echo "ASCII_DOC_LINT_REPORT=$REPORT_FILE" >> "$GITHUB_ENV"
117+
echo "ASCII_DOC_LINT_STATUS=$STATUS" >> "$GITHUB_ENV"
118+
if [ "$STATUS" -ne 0 ]; then
119+
echo "docToolchain AsciiDoc linter exited with status $STATUS" >&2
120+
fi
121+
89122
- name: Build Developer Guide HTML and PDF
90123
run: |
91124
set -euo pipefail
@@ -135,6 +168,65 @@ jobs:
135168
rm -f "$GENERATED_COVER_SVG"
136169
fi
137170
171+
- name: Install Vale
172+
run: |
173+
set -euo pipefail
174+
VALE_VERSION="3.13.0"
175+
VALE_ARCHIVE="vale_${VALE_VERSION}_Linux_64-bit.tar.gz"
176+
curl -fsSL -o "$VALE_ARCHIVE" "https://github.com/errata-ai/vale/releases/download/v${VALE_VERSION}/${VALE_ARCHIVE}"
177+
tar -xzf "$VALE_ARCHIVE"
178+
sudo mv vale /usr/local/bin/vale
179+
rm -f "$VALE_ARCHIVE"
180+
181+
- name: Sync Vale styles
182+
run: |
183+
set -euo pipefail
184+
vale sync --config docs/developer-guide/.vale.ini
185+
186+
- name: Run Vale style linter
187+
run: |
188+
set -euo pipefail
189+
REPORT_DIR="build/developer-guide/reports"
190+
REPORT_FILE="${REPORT_DIR}/vale-report.json"
191+
mkdir -p "$REPORT_DIR"
192+
vale --config docs/developer-guide/.vale.ini --output=JSON docs/developer-guide > "$REPORT_FILE"
193+
echo "VALE_REPORT=$REPORT_FILE" >> "$GITHUB_ENV"
194+
195+
- name: Check for unused developer guide images
196+
run: |
197+
set -euo pipefail
198+
REPORT_DIR="build/developer-guide/reports"
199+
JSON_REPORT="${REPORT_DIR}/unused-images.json"
200+
TEXT_REPORT="${REPORT_DIR}/unused-images.txt"
201+
mkdir -p "$REPORT_DIR"
202+
python3 scripts/developer-guide/find_unused_images.py docs/developer-guide --output "$JSON_REPORT" | tee "$TEXT_REPORT"
203+
echo "UNUSED_IMAGES_JSON=$JSON_REPORT" >> "$GITHUB_ENV"
204+
echo "UNUSED_IMAGES_TEXT=$TEXT_REPORT" >> "$GITHUB_ENV"
205+
206+
- name: Summarize AsciiDoc linter findings
207+
id: summarize_asciidoc_lint
208+
run: |
209+
python3 scripts/developer-guide/summarize_reports.py ascii \
210+
--report "${ASCII_DOC_LINT_REPORT}" \
211+
--status "${ASCII_DOC_LINT_STATUS:-0}" \
212+
--output "${GITHUB_OUTPUT}"
213+
214+
- name: Summarize Vale findings
215+
id: summarize_vale
216+
run: |
217+
python3 scripts/developer-guide/summarize_reports.py vale \
218+
--report "${VALE_REPORT}" \
219+
--output "${GITHUB_OUTPUT}"
220+
221+
- name: Summarize unused image findings
222+
id: summarize_unused_images
223+
run: |
224+
python3 scripts/developer-guide/summarize_reports.py unused-images \
225+
--report "${UNUSED_IMAGES_JSON}" \
226+
--output "${GITHUB_OUTPUT}" \
227+
--details-key details \
228+
--preview-limit 10
229+
138230
- name: Upload HTML artifact
139231
uses: actions/upload-artifact@v4
140232
with:
@@ -149,9 +241,37 @@ jobs:
149241
path: build/developer-guide/pdf/developer-guide.pdf
150242
if-no-files-found: error
151243

244+
- name: Upload AsciiDoc linter report
245+
uses: actions/upload-artifact@v4
246+
with:
247+
name: developer-guide-asciidoc-lint
248+
path: ${{ env.ASCII_DOC_LINT_REPORT }}
249+
if-no-files-found: warn
250+
251+
- name: Upload Vale report
252+
uses: actions/upload-artifact@v4
253+
with:
254+
name: developer-guide-vale-report
255+
path: ${{ env.VALE_REPORT }}
256+
if-no-files-found: warn
257+
258+
- name: Upload unused image report
259+
uses: actions/upload-artifact@v4
260+
with:
261+
name: developer-guide-unused-images
262+
path: |
263+
${{ env.UNUSED_IMAGES_JSON }}
264+
${{ env.UNUSED_IMAGES_TEXT }}
265+
if-no-files-found: warn
266+
152267
- name: Comment with artifact download links
153268
if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }}
154269
uses: actions/github-script@v7
270+
env:
271+
ASCII_SUMMARY: ${{ steps.summarize_asciidoc_lint.outputs.summary }}
272+
VALE_SUMMARY: ${{ steps.summarize_vale.outputs.summary }}
273+
UNUSED_SUMMARY: ${{ steps.summarize_unused_images.outputs.summary }}
274+
UNUSED_DETAILS: ${{ steps.summarize_unused_images.outputs.details }}
155275
with:
156276
github-token: ${{ secrets.GITHUB_TOKEN }}
157277
script: |
@@ -183,22 +303,76 @@ jobs:
183303
per_page: 100
184304
});
185305
186-
const links = [];
306+
const artifactLinks = new Map();
187307
for (const artifact of artifacts.data.artifacts) {
188-
if (artifact.name === 'developer-guide-html') {
189-
links.push(`- [Developer Guide HTML package](https://github.com/${owner}/${repo}/actions/runs/${runId}/artifacts/${artifact.id})`);
190-
}
191-
if (artifact.name === 'developer-guide-pdf') {
192-
links.push(`- [Developer Guide PDF](https://github.com/${owner}/${repo}/actions/runs/${runId}/artifacts/${artifact.id})`);
193-
}
308+
artifactLinks.set(
309+
artifact.name,
310+
`https://github.com/${owner}/${repo}/actions/runs/${runId}/artifacts/${artifact.id}`
311+
);
312+
}
313+
314+
const links = [];
315+
if (artifactLinks.has('developer-guide-html')) {
316+
links.push(`- [Developer Guide HTML package](${artifactLinks.get('developer-guide-html')})`);
317+
}
318+
if (artifactLinks.has('developer-guide-pdf')) {
319+
links.push(`- [Developer Guide PDF](${artifactLinks.get('developer-guide-pdf')})`);
320+
}
321+
if (artifactLinks.has('developer-guide-asciidoc-lint')) {
322+
links.push(`- [AsciiDoc linter report](${artifactLinks.get('developer-guide-asciidoc-lint')})`);
323+
}
324+
if (artifactLinks.has('developer-guide-vale-report')) {
325+
links.push(`- [Vale report](${artifactLinks.get('developer-guide-vale-report')})`);
326+
}
327+
if (artifactLinks.has('developer-guide-unused-images')) {
328+
links.push(`- [Unused image report](${artifactLinks.get('developer-guide-unused-images')})`);
194329
}
195330
196331
if (!links.length) {
197332
console.log('No artifacts found to report.');
198333
return;
199334
}
200335
201-
const body = `${marker}\nDeveloper Guide build artifacts are available for download from this workflow run:\n\n${links.join('\n')}\n`;
336+
const qualityLines = [];
337+
const asciiSummary = process.env.ASCII_SUMMARY?.trim();
338+
const valeSummary = process.env.VALE_SUMMARY?.trim();
339+
const unusedSummary = process.env.UNUSED_SUMMARY?.trim();
340+
const asciiLink = artifactLinks.get('developer-guide-asciidoc-lint');
341+
const valeLink = artifactLinks.get('developer-guide-vale-report');
342+
const unusedLink = artifactLinks.get('developer-guide-unused-images');
343+
344+
if (asciiSummary) {
345+
qualityLines.push(`- AsciiDoc linter: ${asciiSummary}${asciiLink ? ` ([report](${asciiLink}))` : ''}`);
346+
}
347+
if (valeSummary) {
348+
qualityLines.push(`- Vale: ${valeSummary}${valeLink ? ` ([report](${valeLink}))` : ''}`);
349+
}
350+
if (unusedSummary) {
351+
qualityLines.push(`- Image references: ${unusedSummary}${unusedLink ? ` ([report](${unusedLink}))` : ''}`);
352+
}
353+
354+
let unusedDetails = process.env.UNUSED_DETAILS ? process.env.UNUSED_DETAILS.split('\n') : [];
355+
unusedDetails = unusedDetails.filter(Boolean);
356+
const detailsSection = unusedDetails.length
357+
? `\nUnused image preview:\n\n${unusedDetails.map(line => ` ${line}`).join('\n')}\n`
358+
: '';
359+
360+
const sections = [
361+
`${marker}`,
362+
'Developer Guide build artifacts are available for download from this workflow run:',
363+
'',
364+
links.join('\n')
365+
];
366+
367+
if (qualityLines.length) {
368+
sections.push('', 'Developer Guide quality checks:', '', qualityLines.join('\n'));
369+
}
370+
371+
if (detailsSection) {
372+
sections.push(detailsSection.trimEnd());
373+
}
374+
375+
const body = sections.join('\n') + '\n';
202376
const comments = await github.rest.issues.listComments({
203377
owner,
204378
repo,

docs/developer-guide/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
book-cover.generated.svg
22
book-cover.generated.png
3+
styles/

docs/developer-guide/.vale.ini

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
StylesPath = styles
2+
MinAlertLevel = suggestion
3+
Packages = Vale
4+
5+
[*.{adoc,asciidoc}]
6+
BasedOnStyles = Vale

docs/developer-guide/Config.groovy

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
outputPath = 'build'
2+
3+
inputPath = '.'
4+
5+
inputFiles = [
6+
[file: 'developer-guide.asciidoc', formats: ['html']]
7+
]
8+
9+
imageDirs = [
10+
'img'
11+
]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/usr/bin/env python3
2+
"""Identify unreferenced images in the developer guide."""
3+
from __future__ import annotations
4+
5+
import argparse
6+
import json
7+
from pathlib import Path
8+
from typing import Iterable, List
9+
10+
ASCIIDOC_EXTENSIONS = {".adoc", ".asciidoc"}
11+
12+
13+
def iter_text_files(root: Path) -> Iterable[Path]:
14+
for path in root.rglob("*"):
15+
if path.is_file() and path.suffix.lower() in ASCIIDOC_EXTENSIONS:
16+
yield path
17+
18+
19+
def main() -> None:
20+
parser = argparse.ArgumentParser(description=__doc__)
21+
parser.add_argument("doc_root", type=Path, help="Path to the developer guide root directory")
22+
parser.add_argument(
23+
"--image-dir",
24+
type=Path,
25+
default=None,
26+
help="Directory containing images (defaults to <doc_root>/img)",
27+
)
28+
parser.add_argument(
29+
"--output",
30+
type=Path,
31+
default=None,
32+
help="Optional path to write a JSON report",
33+
)
34+
args = parser.parse_args()
35+
36+
doc_root = args.doc_root.resolve()
37+
image_dir = (args.image_dir or (doc_root / "img")).resolve()
38+
39+
if not image_dir.exists():
40+
raise SystemExit(f"Image directory '{image_dir}' does not exist")
41+
42+
adoc_files = list(iter_text_files(doc_root))
43+
contents = [path.read_text(encoding="utf-8", errors="ignore") for path in adoc_files]
44+
45+
unused: List[str] = []
46+
for image_path in sorted(image_dir.rglob("*")):
47+
if not image_path.is_file():
48+
continue
49+
rel_path = image_path.relative_to(doc_root).as_posix()
50+
if any(rel_path in text for text in contents):
51+
continue
52+
# Also fall back to checking just the file name to catch references that rely on imagesdir.
53+
filename = image_path.name
54+
if any(filename in text for text in contents):
55+
continue
56+
unused.append(rel_path)
57+
58+
report = {"unused_images": unused}
59+
60+
if args.output:
61+
args.output.parent.mkdir(parents=True, exist_ok=True)
62+
args.output.write_text(json.dumps(report, indent=2), encoding="utf-8")
63+
64+
if unused:
65+
print("Unused images detected:")
66+
for rel_path in unused:
67+
print(f" - {rel_path}")
68+
else:
69+
print("No unused images found.")
70+
71+
72+
if __name__ == "__main__":
73+
main()

0 commit comments

Comments
 (0)