Skip to content

Commit ceb0404

Browse files
committed
Merge branch '3.7-dev' into 3.8-dev
2 parents 1ac7e01 + b75c29d commit ceb0404

82 files changed

Lines changed: 4385 additions & 120 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bin/process-docs.sh

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ set -e
3434
cd "$(dirname "$0")/.."
3535
TP_HOME="$(pwd)"
3636

37+
# The docs build renders every book to both HTML and Markdown in a single Maven JVM (AsciidoctorJ
38+
# runs in-process), so the heap must hold the parsed AST + JRuby runtime for ~two dozen executions.
39+
# Raise the heap unless the caller already set one, to avoid OutOfMemoryError during rendering.
40+
if [[ "${MAVEN_OPTS:-}" != *-Xmx* ]]; then
41+
export MAVEN_OPTS="${MAVEN_OPTS:-} -Xmx8g"
42+
fi
43+
3744
# ---------------------------------------------------------------------------
3845
# Argument parsing
3946
# ---------------------------------------------------------------------------
@@ -75,6 +82,50 @@ trap cleanup EXIT
7582
trap 'cleanup; exit 130' INT
7683
trap 'cleanup; exit 143' TERM
7784

85+
# ---------------------------------------------------------------------------
86+
# Port readiness probe
87+
# ---------------------------------------------------------------------------
88+
# Returns 0 if a TCP port on the given host is accepting connections. Prefer nc when it is on the
89+
# PATH, but fall back to bash's /dev/tcp so the build still works on hosts without netcat (some
90+
# minimal container images and sandboxes ship no nc). Both branches are silent.
91+
port_open() {
92+
local host="$1" port="$2"
93+
if command -v nc >/dev/null 2>&1; then
94+
nc -z "${host}" "${port}" 2>/dev/null
95+
else
96+
(exec 3<>"/dev/tcp/${host}/${port}") 2>/dev/null && exec 3>&- 2>/dev/null
97+
fi
98+
}
99+
100+
# ---------------------------------------------------------------------------
101+
# Markdown split (agentdocsspec.com)
102+
# ---------------------------------------------------------------------------
103+
# After the Markdown mirror is rendered under target/docs/markdown/, split each rendered book into
104+
# pages driven by llms-summary markers and rewrite intra-book links. Runs MarkdownSplitter from the
105+
# tinkeradoc-extension classes (built by the asciidoc profile). The split is summary-driven: a
106+
# section is a page iff it has an llms-summary. --strict fails the build on any page that exceeds
107+
# the size budget and is not marked allow-oversize, so uncurated/oversized content is surfaced.
108+
split_markdown() {
109+
local md_root="target/docs/markdown"
110+
[ -d "${md_root}" ] || return 0
111+
local ext_classes="docs/tinkeradoc-extension/target/classes"
112+
if [ ! -d "${ext_classes}" ]; then
113+
echo "WARNING: ${ext_classes} not found; skipping Markdown split."
114+
return 0
115+
fi
116+
# The splitter is pure Java, so the extension classes alone suffice on the classpath.
117+
local books
118+
books=$(find "${md_root}" -name index.md 2>/dev/null)
119+
[ -z "${books}" ] && return 0
120+
echo "Splitting Markdown books into agent-sized pages (summary-driven)..."
121+
# shellcheck disable=SC2086
122+
java -cp "${ext_classes}" org.apache.tinkerpop.tinkeradoc.MarkdownSplitter --strict ${books}
123+
124+
# Generate the llms.txt discovery index over the split pages (agentdocsspec.com).
125+
echo "Generating llms.txt discovery index..."
126+
java -cp "${ext_classes}" org.apache.tinkerpop.tinkeradoc.LlmsTxtGenerator "${md_root}"
127+
}
128+
78129
# ---------------------------------------------------------------------------
79130
# Dry-run mode
80131
# ---------------------------------------------------------------------------
@@ -87,7 +138,9 @@ if [ "${MODE}" == "dry" ]; then
87138
cp -r docs/{static,stylesheets} target/postprocess-asciidoc/
88139
cp -r docs/src/* target/postprocess-asciidoc/
89140
mvn process-resources -pl . -Dasciidoc -Dgremlin.docs.dryrun=true
90-
exit $?
141+
ec=$?
142+
[ ${ec} -eq 0 ] && split_markdown
143+
exit ${ec}
91144
fi
92145

93146
# ---------------------------------------------------------------------------
@@ -226,7 +279,7 @@ mkdir -p target
226279
# server to fail binding while the readiness check still passes -- the console
227280
# then connects to the wrong service and WebSocket handshakes fail, dumping
228281
# large stacktraces into the rendered docs.
229-
if nc -z localhost 8182 2>/dev/null; then
282+
if port_open 127.0.0.1 8182; then
230283
echo "ERROR: Port 8182 is already in use by another process."
231284
echo " Gremlin Server needs this port for the docs ':remote' examples."
232285
echo " Identify the process with 'lsof -nP -iTCP:8182' and stop it,"
@@ -248,7 +301,7 @@ for i in $(seq 1 30); do
248301
echo "ERROR: Gremlin Server process exited during startup. See target/gremlin-server-docs.log"
249302
exit 1
250303
fi
251-
if nc -z localhost 8182 2>/dev/null; then
304+
if port_open 127.0.0.1 8182; then
252305
echo " ready."
253306
break
254307
fi
@@ -262,7 +315,7 @@ for i in $(seq 1 30); do
262315
done
263316

264317
# 6. Start Gephi mock (port 8080)
265-
if ! nc -z localhost 8080 2>/dev/null; then
318+
if ! port_open 127.0.0.1 8080; then
266319
echo "Starting Gephi mock on port 8080..."
267320
bin/gephi-mock.py > /dev/null 2>&1 &
268321
GEPHI_MOCK_PID=$!
@@ -290,7 +343,8 @@ ec=$?
290343
set -e
291344

292345
if [ ${ec} -eq 0 ]; then
293-
echo "Documentation build complete. Output: target/docs/htmlsingle/"
346+
split_markdown
347+
echo "Documentation build complete. Output: target/docs/htmlsingle/ and target/docs/markdown/"
294348
else
295349
echo "ERROR: Documentation build failed."
296350
fi

bin/publish-docs.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,27 @@ mkdir -p ../dotnetdocs/_site
6464
rm -rf ../dotnetdocs/_site/*
6565
cp -R ../../gremlin-dotnet/src/_site/ ../dotnetdocs/_site/
6666

67+
# Stage the agent-friendly Markdown mirror + llms.txt (agentdocsspec.com) alongside the HTML, so
68+
# each page is reachable at both docs/<version>/<path>.html and .../<path>.md, and the discovery
69+
# index is served from the site root at /llms.txt. Copy the whole tree into the HTML output area
70+
# before diffing so the existing docs diff/copy/add loop below carries the .md files too.
71+
#
72+
# Before staging, regenerate the per-version llms.txt (docs/<version>/llms.txt) with ABSOLUTE URLs:
73+
# the spec's `llms-txt-links-resolve` / `-links-markdown` checks only recognize full http(s):// URLs
74+
# (relative paths are treated as "no links"). The local build's llms.txt intentionally uses relative
75+
# links for offline inspection; publishing rewrites them to the canonical site URLs.
76+
SITE_DOCS_URL="https://tinkerpop.apache.org/docs"
77+
PUBLISH_EXT_CLASSES="../../docs/tinkeradoc-extension/target/classes"
78+
if [ -d ../docs/markdown ]; then
79+
if [ -d "${PUBLISH_EXT_CLASSES}" ]; then
80+
echo "Regenerating per-version llms.txt with absolute URLs..."
81+
java -cp "${PUBLISH_EXT_CLASSES}" org.apache.tinkerpop.tinkeradoc.LlmsTxtGenerator \
82+
--prefix "${SITE_DOCS_URL}/${VERSION}/" --out ../docs/markdown/llms.txt ../docs/markdown
83+
fi
84+
echo "Staging Markdown mirror into htmlsingle for publish..."
85+
cp -R ../docs/markdown/. ../docs/htmlsingle/
86+
fi
87+
6788
diff -rq -I '^Last updated' docs/${VERSION}/ ../docs/htmlsingle/ | awk -f ../../bin/publish-docs.awk | sed 's/^\(.\) \//\1 /g' > ../publish-docs.docs
6889
diff -rq -I 'Generated by javadoc' -I '^<meta name="date"' javadocs/${VERSION}/ ../site/apidocs/ | awk -f ../../bin/publish-docs.awk | sed 's/^\(.\) \//\1 /g' > ../publish-docs.javadocs
6990
diff -rq -I 'Generated by jsdoc' -I '^<meta name="date"' jsdocs/${VERSION}/ ../jsdocs/doc/ | awk -f ../../bin/publish-docs.awk | sed 's/^\(.\) \//\1 /g' > ../publish-docs.jsdocs
@@ -103,6 +124,17 @@ do
103124
fi
104125
done
105126

127+
# Generate the site-root /llms.txt (agentdocsspec.com discovery index) with ABSOLUTE links into the
128+
# versioned docs tree, matching the per-page "> ... see [llms.txt](/llms.txt)" pointer. Absolute
129+
# URLs are required for the spec's link-resolution checks. This index lives at the site root; the
130+
# per-version llms.txt (also absolute now) is published alongside the pages via the loop above.
131+
if [ -d "../docs/markdown" ] && [ -d "${PUBLISH_EXT_CLASSES}" ]; then
132+
echo "Generating site-root llms.txt..."
133+
java -cp "${PUBLISH_EXT_CLASSES}" org.apache.tinkerpop.tinkeradoc.LlmsTxtGenerator \
134+
--prefix "${SITE_DOCS_URL}/${VERSION}/" --out llms.txt ../docs/markdown
135+
${SVN_CMD} add --force llms.txt 2>/dev/null || true
136+
fi
137+
106138
pushd "docs/${VERSION}/"; cat ../../../publish-docs.docs | awk '/^A/ {print $2}' | grep -v '.graffle$' | xargs --no-run-if-empty svn add --parents; popd
107139
pushd "javadocs/${VERSION}/"; cat ../../../publish-docs.javadocs | awk '/^A/ {print $2}' | xargs --no-run-if-empty svn add --parents; popd
108140
pushd "jsdocs/${VERSION}/"; cat ../../../publish-docs.jsdocs | awk '/^A/ {print $2}' | xargs --no-run-if-empty svn add --parents; popd

bin/validate-llms-txt.sh

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#!/bin/bash
2+
#
3+
# Licensed to the Apache Software Foundation (ASF) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The ASF licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
#
20+
# Validate the agent-friendly documentation (agentdocsspec.com) — the Markdown mirror, the
21+
# llms.txt discovery index, page sizes, link resolution, and the per-page directive — using the
22+
# spec's own tool, `npx afdocs check`.
23+
#
24+
# Usage:
25+
# bin/validate-llms-txt.sh # check the published current docs
26+
# # (https://tinkerpop.apache.org/docs/current/)
27+
# bin/validate-llms-txt.sh -v 3.7.7 # check a published version
28+
# bin/validate-llms-txt.sh --version 3.7.7 # (https://tinkerpop.apache.org/docs/3.7.7/)
29+
# bin/validate-llms-txt.sh -v local # check the LOCAL build: replicate the publish layout
30+
# # (markdown + html + images together) under
31+
# # target/docs/serve/, serve it, and check localhost
32+
#
33+
# Any extra arguments after the mode are passed through to `afdocs check` (e.g. --fixes, --verbose,
34+
# --format json, --skip-checks ...).
35+
#
36+
# NOTE: afdocs requires Node.js >= 22; this script warns if the active node is older. A plain local
37+
# static server cannot exercise the hosting-dependent checks (content-negotiation, redirects,
38+
# soft-404, cache headers, auth) — those are only meaningful against the real published site.
39+
40+
set -e
41+
42+
cd "$(dirname "$0")/.."
43+
TP_HOME="$(pwd)"
44+
45+
BASE_URL="https://tinkerpop.apache.org/docs"
46+
VERSION="current"
47+
48+
# ---------------------------------------------------------------------------
49+
# Argument parsing
50+
# ---------------------------------------------------------------------------
51+
PASSTHROUGH=()
52+
while [[ $# -gt 0 ]]; do
53+
case "$1" in
54+
-v|--version) VERSION="$2"; shift 2 ;;
55+
-h|--help)
56+
sed -n '19,40p' "$0" | sed 's/^# \{0,1\}//'
57+
exit 0 ;;
58+
*) PASSTHROUGH+=("$1"); shift ;;
59+
esac
60+
done
61+
62+
# ---------------------------------------------------------------------------
63+
# Node version advisory (afdocs requires >= 22)
64+
# ---------------------------------------------------------------------------
65+
if command -v node >/dev/null 2>&1; then
66+
NODE_MAJOR=$(node -v | sed 's/^v//; s/\..*//')
67+
if [ "${NODE_MAJOR:-0}" -lt 22 ]; then
68+
echo "WARNING: afdocs requires Node.js >= 22 but found $(node -v)."
69+
echo " Results may be unreliable. Install a newer node (e.g. 'mise install node@22')."
70+
fi
71+
else
72+
echo "ERROR: node/npx not found on PATH; afdocs needs Node.js >= 22."
73+
exit 1
74+
fi
75+
76+
# ---------------------------------------------------------------------------
77+
# Local mode: replicate the publish layout and serve it
78+
# ---------------------------------------------------------------------------
79+
HTTP_PID=""
80+
cleanup() {
81+
set +e
82+
[ -n "${HTTP_PID}" ] && kill "${HTTP_PID}" 2>/dev/null
83+
}
84+
trap cleanup EXIT INT TERM
85+
86+
run_afdocs() {
87+
local url="$1"; shift
88+
local extra=("$@")
89+
echo "Running: npx --yes afdocs check ${url} ${extra[*]} ${PASSTHROUGH[*]}"
90+
echo "-----------------------------------------------------------------------"
91+
npx --yes afdocs@latest check "${url}" "${extra[@]}" "${PASSTHROUGH[@]}"
92+
}
93+
94+
if [ "${VERSION}" == "local" ]; then
95+
MD_DIR="target/docs/markdown"
96+
HTML_DIR="target/docs/htmlsingle"
97+
SERVE_DIR="target/docs/serve"
98+
99+
if [ ! -d "${MD_DIR}" ] || [ ! -f "${MD_DIR}/llms.txt" ]; then
100+
echo "ERROR: No local Markdown docs found at ${MD_DIR} (with llms.txt)."
101+
echo " Generate them first, e.g.: bin/process-docs.sh --dryRun"
102+
echo " (a --dryRun build produces the Markdown mirror + llms.txt without a live server;"
103+
echo " a full 'bin/process-docs.sh' additionally fills in executed ==> output.)"
104+
exit 1
105+
fi
106+
107+
echo "Replicating publish layout into ${SERVE_DIR}/ (markdown + html + images together)..."
108+
rm -rf "${SERVE_DIR}"
109+
mkdir -p "${SERVE_DIR}"
110+
# HTML first (brings images/, stylesheets, and the .html pages), then overlay the Markdown mirror
111+
# so .md sits beside .html and llms.txt lands at the served root — mirroring bin/publish-docs.sh.
112+
if [ -d "${HTML_DIR}" ]; then
113+
cp -R "${HTML_DIR}/." "${SERVE_DIR}/"
114+
else
115+
echo "NOTE: ${HTML_DIR} not found; serving Markdown only (HTML/parity checks will be limited)."
116+
fi
117+
cp -R "${MD_DIR}/." "${SERVE_DIR}/"
118+
119+
# Pick a free port up front so we can bake the absolute base URL into llms.txt before serving.
120+
PORT=$(python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1",0)); print(s.getsockname()[1]); s.close()')
121+
LOCAL_URL="http://127.0.0.1:${PORT}/"
122+
123+
# Regenerate the served llms.txt with ABSOLUTE URLs (http://127.0.0.1:PORT/...). The spec's
124+
# link-resolution/coverage checks only recognize full http(s):// links, so this mirrors what
125+
# publish-docs.sh does with the canonical site URL and lets the local run exercise those checks.
126+
EXT_CLASSES="docs/tinkeradoc-extension/target/classes"
127+
if [ -d "${EXT_CLASSES}" ]; then
128+
echo "Regenerating served llms.txt with absolute URLs for ${LOCAL_URL} ..."
129+
java -cp "${EXT_CLASSES}" org.apache.tinkerpop.tinkeradoc.LlmsTxtGenerator \
130+
--prefix "${LOCAL_URL}" --out "${SERVE_DIR}/llms.txt" "${SERVE_DIR}"
131+
else
132+
echo "NOTE: ${EXT_CLASSES} not found; serving llms.txt as-is (relative links won't satisfy"
133+
echo " the link-resolution/coverage checks). Build the extension to enable absolute URLs."
134+
fi
135+
136+
echo "Serving ${SERVE_DIR}/ at ${LOCAL_URL} ..."
137+
# A threaded server: afdocs issues concurrent requests, and python's default single-threaded
138+
# http.server can drop connections mid-response (undici in afdocs then aborts). ThreadingHTTPServer
139+
# handles the concurrency cleanly.
140+
python3 -c "
141+
import sys, http.server, socketserver, functools
142+
port = int(sys.argv[1]); root = sys.argv[2]
143+
handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory=root)
144+
class T(socketserver.ThreadingMixIn, http.server.HTTPServer):
145+
daemon_threads = True
146+
allow_reuse_address = True
147+
T(('127.0.0.1', port), handler).serve_forever()
148+
" "${PORT}" "${SERVE_DIR}" >/dev/null 2>&1 &
149+
HTTP_PID=$!
150+
151+
# Wait for readiness (loopback works within this single script invocation).
152+
for i in $(seq 1 30); do
153+
if curl -sf "http://127.0.0.1:${PORT}/llms.txt" >/dev/null 2>&1; then break; fi
154+
if ! kill -0 "${HTTP_PID}" 2>/dev/null; then
155+
echo "ERROR: local web server exited during startup."; exit 1
156+
fi
157+
[ "$i" -eq 30 ] && { echo "ERROR: local web server did not become ready."; exit 1; }
158+
sleep 0.5
159+
done
160+
161+
# Local-run options (each skippable by passing your own):
162+
# --canonical-origin: tells afdocs the localhost origin is canonical, so the absolute llms.txt
163+
# links (baked in above) count as same-origin and its link-resolution checks run.
164+
# --max-concurrency 1 / --request-delay: afdocs' HTTP client (undici) can abort on many-page
165+
# concurrent crawls against a lightweight local server; serial requests are reliable.
166+
LOCAL_OPTS=()
167+
[[ " ${PASSTHROUGH[*]} " == *" --canonical-origin "* ]] || LOCAL_OPTS+=(--canonical-origin "http://127.0.0.1:${PORT}")
168+
[[ " ${PASSTHROUGH[*]} " == *" --max-concurrency "* ]] || LOCAL_OPTS+=(--max-concurrency 1)
169+
[[ " ${PASSTHROUGH[*]} " == *" --request-delay "* ]] || LOCAL_OPTS+=(--request-delay 20)
170+
run_afdocs "${LOCAL_URL}" "${LOCAL_OPTS[@]}"
171+
echo "-----------------------------------------------------------------------"
172+
echo "NOTE: hosting-dependent checks (content-negotiation, redirects, soft-404, cache headers,"
173+
echo " auth) are not meaningful against this local static server; validate those against"
174+
echo " the published site."
175+
else
176+
run_afdocs "${BASE_URL}/${VERSION}/"
177+
fi

docs/src/dev/developer/contributing.asciidoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ the appropriate branch on which to submit the pull request so that the documenta
8282
tied to. To view generated documentation locally, read more about environment configurations in the
8383
<<documentation-environment,Documentation Environment>> and <<documentation, Contributor Documentation>> sections.
8484
85+
The reference documentation is also rendered in an agent-friendly form (Markdown plus an `llms.txt` index). When adding
86+
a new section that should be discoverable as its own page, give it an `llms-summary` attribute and keep the resulting
87+
page within the size budget. See <<agent-friendly-documentation,Agent-Friendly Documentation>> for the authoring rules.
88+
8589
For web site changes, the process is largely the same except that the documentation system is HTML based instead of
8690
Asciidoc. The content can be found in the source control tree at link:https://github.com/apache/tinkerpop/tree/master/docs/site[docs/site].
8791
The web site is always published from the `master` branch as it is not bound to a version, so there is no need to

0 commit comments

Comments
 (0)