\n')
+
+
+def render_html(title, license_lines, toc_tree, sections):
+ """Render the full HTML document for a single protocol version."""
+ out = io.StringIO()
+ t_esc = html.escape(title)
+ out.write('\n')
+ out.write('\n')
+ out.write('\n')
+ out.write(' \n')
+ out.write(f' {t_esc}\n')
+ out.write(' \n')
+ out.write('\n')
+ out.write('\n')
+ for line in license_lines:
+ out.write(f' \n')
+ out.write(f'
{t_esc}
\n')
+ out.write('
Table of Contents
\n')
+ out.write(' \n')
+ for sec in sections:
+ lvl = sec['level']
+ num = sec['number']
+ sec_title = html.escape(sec['title'])
+ out.write(f' {num} {sec_title}\n')
+ out.write(f' {sec["body_html"]}\n')
+ out.write('\n')
+ out.write('\n')
+ return out.getvalue()
+
+
+def main(): # pylint: disable=too-many-locals
+ """CLI entrypoint: render one HTML page per spec file plus an asciidoc summary."""
+ parser = argparse.ArgumentParser(
+ description="Generate native-protocol HTML and asciidoc summary from .spec files."
+ )
+ parser.add_argument(
+ '--spec-dir', type=Path, default=Path('.'),
+ help="Directory containing native_protocol_v*.spec files (default: cwd)."
+ )
+ parser.add_argument(
+ '--attach-dir', type=Path, default=Path('modules/cassandra/attachments'),
+ help="Output directory for per-version HTML files."
+ )
+ parser.add_argument(
+ '--summary-adoc', type=Path,
+ default=Path('modules/cassandra/pages/reference/native-protocol.adoc'),
+ help="Output path for the generated asciidoc summary."
+ )
+ args = parser.parse_args()
+
+ spec_dir = args.spec_dir
+ attach_dir = args.attach_dir
+ summary_adoc = args.summary_adoc
+
+ if not spec_dir.is_dir():
+ sys.exit(f"Spec directory does not exist: {spec_dir.resolve()}")
+
+ attach_dir.mkdir(parents=True, exist_ok=True)
+ summary_adoc.parent.mkdir(parents=True, exist_ok=True)
+
+ specs = sorted(
+ (p for p in spec_dir.glob('native_protocol_v*.spec')
+ if _protocol_filename_re.match(p.name)),
+ key=lambda p: int(_protocol_filename_re.match(p.name).group(1)),
+ reverse=True,
+ )
+ if not specs:
+ sys.exit(f"No native_protocol_v*.spec files found in {spec_dir.resolve()}")
+
+ for sp in specs:
+ version = _protocol_filename_re.match(sp.name).group(1)
+ hp = attach_dir / f'native_protocol_v{version}.html'
+ doc = parse_spec_file(sp)
+ toc_tree = build_toc_tree(doc['toc'], {s['number'] for s in doc['sections']})
+ sections = build_sections(doc['sections'])
+ rendered = render_html(doc['title'], doc['license'], toc_tree, sections)
+ hp.write_text(rendered, encoding='utf-8')
+ print(f"-> {hp}")
+
+ nav_js = """[source, js]
+++++
+
+++++
+"""
+
+ html_files = sorted(
+ (p for p in attach_dir.glob('native_protocol_v*.html')
+ if _protocol_filename_re.match(p.name)),
+ key=lambda p: int(_protocol_filename_re.match(p.name).group(1)),
+ reverse=True,
+ )
+ with summary_adoc.open('w', encoding='utf-8') as f:
+ f.write("= Native Protocol Versions\n")
+ f.write(":page-layout: default\n\n")
+ for file in html_files:
+ ver = _protocol_filename_re.match(file.name).group(1)
+ f.write(f"== Native Protocol Version {ver}\n\n")
+ f.write("[source, html]\n++++\n")
+ f.write(f"include::cassandra:attachment${file.name}[Version {ver}]\n")
+ f.write("++++\n\n")
+ f.write(nav_js)
+ print(f"-> {summary_adoc}")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/doc/scripts/gen-native-protocol-docs.sh b/doc/scripts/gen-native-protocol-docs.sh
new file mode 100755
index 0000000000..17bef66e74
--- /dev/null
+++ b/doc/scripts/gen-native-protocol-docs.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+
+[ "x${SCRIPT_DIR:-}" != "x" ] || SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+[ "x${PYTOOL:-}" != "x" ] || PYTOOL="$SCRIPT_DIR/cqlprotodoc.py"
+
+echo "Processing native protocol specs..."
+python3 "$PYTOOL"
+
+echo "Done"
+exit 0
diff --git a/doc/scripts/process-native-protocol-specs-in-docker.sh b/doc/scripts/process-native-protocol-specs-in-docker.sh
deleted file mode 100755
index 332310ab66..0000000000
--- a/doc/scripts/process-native-protocol-specs-in-docker.sh
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/bin/sh
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-[ -f "../build.xml" ] || { echo "build.xml must exist (current directory needs to be doc/ in cassandra repo"; exit 1; }
-[ -f "antora.yml" ] || { echo "antora.yml must exist (current directory needs to be doc/ in cassandra repo"; exit 1; }
-
-# Variables
-GO_VERSION="1.23.1"
-TMPDIR="${TMPDIR:-/tmp}"
-
-check_go_version() {
- if command -v go &>/dev/null; then
- local installed_version=$(go version | awk '{print $3}' | sed 's/go//')
- if [ "$(printf '%s\n' "$GO_VERSION" "$installed_version" | sort -V | head -n1)" = "$GO_VERSION" ]; then
- echo "Detected Go $installed_version (>= $GO_VERSION)"
- return 0
- else
- echo "Detected unsupported Go $installed_version (< $GO_VERSION), please update to supported version."
- fi
- else
- echo "No Go installation detected, please install Go (>= $GO_VERSION)"
- fi
- return 1
-}
-
-if ! check_go_version; then
- echo " Please install/upgrade Golang for 'ant gen-doc', or specify '-Dant.gen-doc.skip=true' to skip this step."
- echo " For download and installation instructions see https://go.dev/doc/install"
- exit 1
-fi
-
-# Step 1: Building the parser
-echo "Building the cqlprotodoc..."
-DIR="$(pwd)"
-cd "${TMPDIR}"
-
-rm -rf "${TMPDIR}/cassandra-website"
-git clone -n --depth=1 --filter=tree:0 https://github.com/apache/cassandra-website
-
-if [ $? != "0" ]; then
- echo "Error occured while cloning https://github.com/apache/cassandra-website"
- exit 1
-fi
-
-cd "${TMPDIR}/cassandra-website"
-git sparse-checkout set --no-cone /cqlprotodoc
-git checkout
-cd "${TMPDIR}/cassandra-website/cqlprotodoc"
-rm -rf "${TMPDIR}/cqlprotodoc"
-go build -o "$TMPDIR"/cqlprotodoc
-
-# Step 2: Process the spec files using the parser
-echo "Processing the .spec files..."
-cd "${DIR}"
-output_dir="modules/cassandra/attachments"
-mkdir -p "${output_dir}"
-"$TMPDIR"/cqlprotodoc . "${output_dir}"
-
-if ! ls ${output_dir}/native_protocol_v*.html > /dev/null 2>&1; then
- echo "failed: No native_protocol_v*.html files generated in ${output_dir}"
- exit 1
-fi
-
-# Step 4: Generate summary file
-summary_file="modules/cassandra/pages/reference/native-protocol.adoc"
-
-# Write the header
-echo "= Native Protocol Versions" > "$summary_file"
-echo ":page-layout: default" >> "$summary_file"
-echo >> "$summary_file"
-
-# Loop through the files from step 2 in reverse version order
-for file in $(ls ${output_dir}/native_protocol_v*.html | sort -r | awk -F/ '{print $NF}'); do
- version=$(echo "$file" | sed -E 's/native_protocol_v([0-9]+)\.html/\1/')
- echo "== Native Protocol Version $version" >> "$summary_file"
- echo >> "$summary_file"
- echo "[source, html]" >> "$summary_file"
- echo "++++" >> "$summary_file"
- echo "include::cassandra:attachment\$$file[Version $version]" >> "$summary_file"
- echo "++++" >> "$summary_file"
- echo >> "$summary_file"
-done
-
-# Navigation setup
-echo "[source, js]" >> "$summary_file"
-echo "++++" >> "$summary_file"
-echo "" >> "$summary_file"
-
-
-# Step 3: Cleanup - Remove the Cassandra and parser directories
-echo "Cleaning up..."
-cd "${DIR}"
-rm -rf "${TMPDIR}/cassandra-website" "${TMPDIR}/cqlprotodoc" 2>/dev/null
-
-echo "Script completed successfully."