8484
8585 - name : Install Asciidoctor tooling
8686 run : |
87- gem install --no-document asciidoctor asciidoctor-pdf
87+ gem install --no-document asciidoctor asciidoctor-pdf rouge
88+
89+ - name : Run Asciidoctor lint
90+ run : |
91+ set -euo pipefail
92+ REPORT_DIR="build/developer-guide/reports"
93+ REPORT_FILE="${REPORT_DIR}/asciidoc-lint-report.txt"
94+ mkdir -p "$REPORT_DIR"
95+ set +e
96+ asciidoctor \
97+ --require rouge \
98+ --failure-level WARN \
99+ --verbose \
100+ --trace \
101+ -o /dev/null \
102+ docs/developer-guide/developer-guide.asciidoc \
103+ 2>&1 | tee "$REPORT_FILE"
104+ STATUS=${PIPESTATUS[0]}
105+ set -e
106+ echo "ASCII_DOC_LINT_REPORT=$REPORT_FILE" >> "$GITHUB_ENV"
107+ echo "ASCII_DOC_LINT_STATUS=$STATUS" >> "$GITHUB_ENV"
108+ if [ "$STATUS" -ne 0 ]; then
109+ echo "Asciidoctor exited with status $STATUS" >&2
110+ fi
88111
89112 - name : Build Developer Guide HTML and PDF
90113 run : |
@@ -135,6 +158,76 @@ jobs:
135158 rm -f "$GENERATED_COVER_SVG"
136159 fi
137160
161+ - name : Install Vale
162+ run : |
163+ set -euo pipefail
164+ VALE_VERSION="3.13.0"
165+ VALE_ARCHIVE="vale_${VALE_VERSION}_Linux_64-bit.tar.gz"
166+ curl -fsSL -o "$VALE_ARCHIVE" "https://github.com/errata-ai/vale/releases/download/v${VALE_VERSION}/${VALE_ARCHIVE}"
167+ tar -xzf "$VALE_ARCHIVE"
168+ sudo mv vale /usr/local/bin/vale
169+ rm -f "$VALE_ARCHIVE"
170+
171+ - name : Sync Vale styles
172+ run : |
173+ set -euo pipefail
174+ vale sync --config docs/developer-guide/.vale.ini
175+
176+ - name : Run Vale style linter
177+ run : |
178+ set -euo pipefail
179+ REPORT_DIR="build/developer-guide/reports"
180+ REPORT_FILE="${REPORT_DIR}/vale-report.json"
181+ HTML_REPORT="${REPORT_DIR}/vale-report.html"
182+ mkdir -p "$REPORT_DIR"
183+ set +e
184+ vale --config docs/developer-guide/.vale.ini --output=JSON docs/developer-guide > "$REPORT_FILE"
185+ STATUS=$?
186+ set -e
187+ python3 scripts/developer-guide/vale_report_to_html.py --input "$REPORT_FILE" --output "$HTML_REPORT"
188+ echo "VALE_REPORT=$REPORT_FILE" >> "$GITHUB_ENV"
189+ echo "VALE_HTML_REPORT=$HTML_REPORT" >> "$GITHUB_ENV"
190+ echo "VALE_STATUS=$STATUS" >> "$GITHUB_ENV"
191+ if [ "$STATUS" -ne 0 ]; then
192+ echo "Vale exited with status $STATUS" >&2
193+ fi
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+ --status "${VALE_STATUS:-0}" \
220+ --output "${GITHUB_OUTPUT}"
221+
222+ - name : Summarize unused image findings
223+ id : summarize_unused_images
224+ run : |
225+ python3 scripts/developer-guide/summarize_reports.py unused-images \
226+ --report "${UNUSED_IMAGES_JSON}" \
227+ --output "${GITHUB_OUTPUT}" \
228+ --details-key details \
229+ --preview-limit 10
230+
138231 - name : Upload HTML artifact
139232 uses : actions/upload-artifact@v4
140233 with :
@@ -149,9 +242,39 @@ jobs:
149242 path : build/developer-guide/pdf/developer-guide.pdf
150243 if-no-files-found : error
151244
245+ - name : Upload AsciiDoc linter report
246+ uses : actions/upload-artifact@v4
247+ with :
248+ name : developer-guide-asciidoc-lint
249+ path : ${{ env.ASCII_DOC_LINT_REPORT }}
250+ if-no-files-found : warn
251+
252+ - name : Upload Vale report
253+ uses : actions/upload-artifact@v4
254+ with :
255+ name : developer-guide-vale-report
256+ path : |
257+ ${{ env.VALE_REPORT }}
258+ ${{ env.VALE_HTML_REPORT }}
259+ if-no-files-found : warn
260+
261+ - name : Upload unused image report
262+ uses : actions/upload-artifact@v4
263+ with :
264+ name : developer-guide-unused-images
265+ path : |
266+ ${{ env.UNUSED_IMAGES_JSON }}
267+ ${{ env.UNUSED_IMAGES_TEXT }}
268+ if-no-files-found : warn
269+
152270 - name : Comment with artifact download links
153271 if : ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }}
154272 uses : actions/github-script@v7
273+ env :
274+ ASCII_SUMMARY : ${{ steps.summarize_asciidoc_lint.outputs.summary }}
275+ VALE_SUMMARY : ${{ steps.summarize_vale.outputs.summary }}
276+ UNUSED_SUMMARY : ${{ steps.summarize_unused_images.outputs.summary }}
277+ UNUSED_DETAILS : ${{ steps.summarize_unused_images.outputs.details }}
155278 with :
156279 github-token : ${{ secrets.GITHUB_TOKEN }}
157280 script : |
@@ -183,22 +306,76 @@ jobs:
183306 per_page: 100
184307 });
185308
186- const links = [] ;
309+ const artifactLinks = new Map() ;
187310 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- }
311+ artifactLinks.set(
312+ artifact.name,
313+ `https://github.com/${owner}/${repo}/actions/runs/${runId}/artifacts/${artifact.id}`
314+ );
315+ }
316+
317+ const links = [];
318+ if (artifactLinks.has('developer-guide-html')) {
319+ links.push(`- [Developer Guide HTML package](${artifactLinks.get('developer-guide-html')})`);
320+ }
321+ if (artifactLinks.has('developer-guide-pdf')) {
322+ links.push(`- [Developer Guide PDF](${artifactLinks.get('developer-guide-pdf')})`);
323+ }
324+ if (artifactLinks.has('developer-guide-asciidoc-lint')) {
325+ links.push(`- [AsciiDoc linter report](${artifactLinks.get('developer-guide-asciidoc-lint')})`);
326+ }
327+ if (artifactLinks.has('developer-guide-vale-report')) {
328+ links.push(`- [Vale report](${artifactLinks.get('developer-guide-vale-report')})`);
329+ }
330+ if (artifactLinks.has('developer-guide-unused-images')) {
331+ links.push(`- [Unused image report](${artifactLinks.get('developer-guide-unused-images')})`);
194332 }
195333
196334 if (!links.length) {
197335 console.log('No artifacts found to report.');
198336 return;
199337 }
200338
201- const body = `${marker}\nDeveloper Guide build artifacts are available for download from this workflow run:\n\n${links.join('\n')}\n`;
339+ const qualityLines = [];
340+ const asciiSummary = process.env.ASCII_SUMMARY?.trim();
341+ const valeSummary = process.env.VALE_SUMMARY?.trim();
342+ const unusedSummary = process.env.UNUSED_SUMMARY?.trim();
343+ const asciiLink = artifactLinks.get('developer-guide-asciidoc-lint');
344+ const valeLink = artifactLinks.get('developer-guide-vale-report');
345+ const unusedLink = artifactLinks.get('developer-guide-unused-images');
346+
347+ if (asciiSummary) {
348+ qualityLines.push(`- AsciiDoc linter: ${asciiSummary}${asciiLink ? ` ([report](${asciiLink}))` : ''}`);
349+ }
350+ if (valeSummary) {
351+ qualityLines.push(`- Vale: ${valeSummary}${valeLink ? ` ([report](${valeLink}))` : ''}`);
352+ }
353+ if (unusedSummary) {
354+ qualityLines.push(`- Image references: ${unusedSummary}${unusedLink ? ` ([report](${unusedLink}))` : ''}`);
355+ }
356+
357+ let unusedDetails = process.env.UNUSED_DETAILS ? process.env.UNUSED_DETAILS.split('\n') : [];
358+ unusedDetails = unusedDetails.filter(Boolean);
359+ const detailsSection = unusedDetails.length
360+ ? `\nUnused image preview:\n\n${unusedDetails.map(line => ` ${line}`).join('\n')}\n`
361+ : '';
362+
363+ const sections = [
364+ `${marker}`,
365+ 'Developer Guide build artifacts are available for download from this workflow run:',
366+ '',
367+ links.join('\n')
368+ ];
369+
370+ if (qualityLines.length) {
371+ sections.push('', 'Developer Guide quality checks:', '', qualityLines.join('\n'));
372+ }
373+
374+ if (detailsSection) {
375+ sections.push(detailsSection.trimEnd());
376+ }
377+
378+ const body = sections.join('\n') + '\n';
202379 const comments = await github.rest.issues.listComments({
203380 owner,
204381 repo,
0 commit comments