@redpanda-data/docs-extensions-and-macros 4.3.0 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/doc-tools.js +328 -0
- package/cli-utils/add-caret-external-links.py +68 -0
- package/cli-utils/beta-from-antora.js +27 -0
- package/cli-utils/generate-cluster-docs.sh +83 -0
- package/cli-utils/install-test-dependencies.sh +158 -0
- package/cli-utils/python-venv.sh +20 -0
- package/cli-utils/start-cluster.sh +53 -0
- package/docker-compose/bootstrap.yml +67 -0
- package/docker-compose/docker-compose.yml +414 -0
- package/docker-compose/generate-profiles.yaml +77 -0
- package/docker-compose/rpk-profile.yaml +24 -0
- package/docker-compose/transactions-schema.json +37 -0
- package/docker-compose/transactions.md +46 -0
- package/docker-compose/transform/README.adoc +73 -0
- package/docker-compose/transform/go.mod +5 -0
- package/docker-compose/transform/go.sum +2 -0
- package/docker-compose/transform/regex.wasm +0 -0
- package/docker-compose/transform/transform.go +122 -0
- package/docker-compose/transform/transform.yaml +33 -0
- package/extension-utils/compute-out.js +38 -0
- package/extension-utils/create-asciidoc-file.js +15 -0
- package/macros/data-template.js +2 -2
- package/package.json +15 -3
- package/tools/docusaurus-to-antora-conversion-scripts/convert-docs.sh +114 -0
- package/tools/docusaurus-to-antora-conversion-scripts/get-file-changes.sh +9 -0
- package/tools/docusaurus-to-antora-conversion-scripts/post-process-asciidoc.js +63 -0
- package/tools/docusaurus-to-antora-conversion-scripts/pre-process-markdown.js +108 -0
- package/tools/fetch-from-github.js +63 -0
- package/tools/gen-rpk-ascii.py +477 -0
- package/tools/get-console-version.js +53 -0
- package/tools/get-redpanda-version.js +53 -0
- package/tools/metrics/metrics.py +199 -0
- package/tools/metrics/requirements.txt +1 -0
- package/tools/property-extractor/Makefile +99 -0
- package/tools/property-extractor/README.adoc +206 -0
- package/tools/property-extractor/definitions.json +245 -0
- package/tools/property-extractor/file_pair.py +7 -0
- package/tools/property-extractor/json-to-asciidoc/generate_docs.py +460 -0
- package/tools/property-extractor/parser.py +224 -0
- package/tools/property-extractor/property_bag.py +4 -0
- package/tools/property-extractor/property_extractor.py +243 -0
- package/tools/property-extractor/requirements.txt +2 -0
- package/tools/property-extractor/tests/transformers_test.py +376 -0
- package/tools/property-extractor/transformers.py +397 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const yaml = require('js-yaml');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const GetLatestConsoleVersion = require('../extensions/version-fetcher/get-latest-console-version.js');
|
|
7
|
+
const { getPrereleaseFromAntora } = require('../cli-utils/beta-from-antora.js');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Fetches and prints the latest Console version and Docker repo.
|
|
11
|
+
* @param {Object} options
|
|
12
|
+
* @param {boolean} options.beta - Return beta version if available
|
|
13
|
+
* @param {boolean} options.fromAntora - Derive beta flag from antora.yml
|
|
14
|
+
*/
|
|
15
|
+
module.exports = async function getConsoleVersion({ beta = false, fromAntora = false }) {
|
|
16
|
+
const owner = 'redpanda-data';
|
|
17
|
+
const repo = 'console';
|
|
18
|
+
const CONSOLE_DOCKER_REPO = repo;
|
|
19
|
+
|
|
20
|
+
// Determine whether to use beta based on antora.yml or flag
|
|
21
|
+
let useBeta = beta;
|
|
22
|
+
if (fromAntora) {
|
|
23
|
+
useBeta = getPrereleaseFromAntora();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Initialize GitHub client
|
|
27
|
+
const { Octokit } = await import('@octokit/rest');
|
|
28
|
+
const octokit = process.env.REDPANDA_GITHUB_TOKEN
|
|
29
|
+
? new Octokit({ auth: process.env.REDPANDA_GITHUB_TOKEN })
|
|
30
|
+
: new Octokit();
|
|
31
|
+
|
|
32
|
+
// Fetch latest release info
|
|
33
|
+
let data;
|
|
34
|
+
try {
|
|
35
|
+
data = await GetLatestConsoleVersion(octokit, owner, repo);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error('Failed to fetch Console version:', err.message);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!data) {
|
|
42
|
+
console.error('No version data returned for Console');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Select the version
|
|
47
|
+
const version = useBeta
|
|
48
|
+
? (data.latestBetaRelease || data.latestStableRelease)
|
|
49
|
+
: data.latestStableRelease;
|
|
50
|
+
|
|
51
|
+
console.log(`CONSOLE_VERSION=${version}`);
|
|
52
|
+
console.log(`CONSOLE_DOCKER_REPO=${CONSOLE_DOCKER_REPO}`);
|
|
53
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const GetLatestRedpandaVersion = require('../extensions/version-fetcher/get-latest-redpanda-version.js');
|
|
4
|
+
const { getPrereleaseFromAntora } = require('../cli-utils/beta-from-antora.js');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Fetches and prints the latest Redpanda version and Docker repository.
|
|
8
|
+
* @param {Object} options
|
|
9
|
+
* @param {boolean} options.beta - Whether to prefer RC (beta) releases
|
|
10
|
+
* @param {boolean} options.fromAntora - Whether to derive beta flag from antora.yml
|
|
11
|
+
*/
|
|
12
|
+
module.exports = async function getRedpandaVersion({ beta = false, fromAntora = false }) {
|
|
13
|
+
const owner = 'redpanda-data';
|
|
14
|
+
const repo = 'redpanda';
|
|
15
|
+
|
|
16
|
+
// Determine whether to treat this as a beta (RC) release
|
|
17
|
+
let useBeta = beta;
|
|
18
|
+
if (fromAntora) {
|
|
19
|
+
useBeta = getPrereleaseFromAntora();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Load Octokit
|
|
23
|
+
const { Octokit } = await import('@octokit/rest');
|
|
24
|
+
const octokit = process.env.REDPANDA_GITHUB_TOKEN
|
|
25
|
+
? new Octokit({ auth: process.env.REDPANDA_GITHUB_TOKEN })
|
|
26
|
+
: new Octokit();
|
|
27
|
+
|
|
28
|
+
// Fetch version data
|
|
29
|
+
let data;
|
|
30
|
+
try {
|
|
31
|
+
data = await GetLatestRedpandaVersion(octokit, owner, repo);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
console.error('Failed to fetch the latest Redpanda version:', err.message);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!data) {
|
|
38
|
+
console.error('No version data returned for Redpanda');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Determine the version string
|
|
43
|
+
const stableVersion = data.latestRedpandaRelease.version;
|
|
44
|
+
const rc = data.latestRcRelease;
|
|
45
|
+
const version = useBeta && rc ? rc.version : stableVersion;
|
|
46
|
+
|
|
47
|
+
// Determine the Docker repository
|
|
48
|
+
const dockerRepo = (useBeta && rc) ? 'redpanda-unstable' : 'redpanda';
|
|
49
|
+
|
|
50
|
+
// Output for downstream consumption
|
|
51
|
+
console.log(`REDPANDA_VERSION=${version}`);
|
|
52
|
+
console.log(`REDPANDA_DOCKER_REPO=${dockerRepo}`);
|
|
53
|
+
};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import requests
|
|
4
|
+
import re
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
'''
|
|
9
|
+
## How it works
|
|
10
|
+
|
|
11
|
+
Fetches metrics from the brokers and parses out the `# HELP` lines, creating:
|
|
12
|
+
|
|
13
|
+
- `metrics.json`
|
|
14
|
+
- `metrics.adoc`
|
|
15
|
+
|
|
16
|
+
These output files are stored in a versioned folder under `docs/autogenerated/<version>/metrics`, based on the major.minor version provided as the first argument.
|
|
17
|
+
|
|
18
|
+
## Prerequisites
|
|
19
|
+
|
|
20
|
+
- **Python 3** & **pip** on your system.
|
|
21
|
+
- A Redpanda cluster running with the `public_metrics` and `metrics` endpoints exposed at http://localhost:19644/
|
|
22
|
+
'''
|
|
23
|
+
|
|
24
|
+
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
|
|
25
|
+
|
|
26
|
+
def fetch_metrics(url):
|
|
27
|
+
"""Fetch metrics from the given URL."""
|
|
28
|
+
try:
|
|
29
|
+
response = requests.get(url)
|
|
30
|
+
response.raise_for_status()
|
|
31
|
+
return response.text
|
|
32
|
+
except requests.RequestException as e:
|
|
33
|
+
logging.error(f"Error fetching metrics from {url}: {e}")
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
def parse_metrics(metrics_text):
|
|
37
|
+
"""
|
|
38
|
+
Parse Prometheus exposition text into a dict:
|
|
39
|
+
metric_name → { description, type, unit, labels: [<label_keys>] }
|
|
40
|
+
|
|
41
|
+
- Strips empty `{}` on unlabelled samples
|
|
42
|
+
- Propagates HELP/TYPE from base metrics onto _bucket, _count, _sum
|
|
43
|
+
- Works regardless of # HELP / # TYPE order
|
|
44
|
+
- Captures # UNIT metadata if present
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
lines = metrics_text.splitlines()
|
|
48
|
+
|
|
49
|
+
# Gather HELP/TYPE metadata in any order
|
|
50
|
+
# Gather HELP/TYPE metadata in any order
|
|
51
|
+
meta = {} # name → { 'description': str, 'type': str, 'unit': str }
|
|
52
|
+
for line in lines:
|
|
53
|
+
if line.startswith("# HELP"):
|
|
54
|
+
m = re.match(r"# HELP\s+(\S+)\s+(.+)", line)
|
|
55
|
+
if m:
|
|
56
|
+
name, desc = m.groups()
|
|
57
|
+
meta.setdefault(name, {})['description'] = desc
|
|
58
|
+
elif line.startswith("# TYPE"):
|
|
59
|
+
m = re.match(r"# TYPE\s+(\S+)\s+(\S+)", line)
|
|
60
|
+
if m:
|
|
61
|
+
name, mtype = m.groups()
|
|
62
|
+
meta.setdefault(name, {})['type'] = mtype
|
|
63
|
+
elif line.startswith("# UNIT"):
|
|
64
|
+
m = re.match(r"# UNIT\s+(\S+)\s+(.+)", line)
|
|
65
|
+
if m:
|
|
66
|
+
name, unit = m.groups()
|
|
67
|
+
meta.setdefault(name, {})['unit'] = unit
|
|
68
|
+
# Collect label keys from _every_ sample line
|
|
69
|
+
label_map = {} # name → set(label_keys)
|
|
70
|
+
for line in lines:
|
|
71
|
+
if line.startswith("#"):
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
# labelled: foo{a="1",b="2"} 42
|
|
75
|
+
m_lbl = re.match(r"^(\S+)\{(.+?)\}\s+(.+)", line)
|
|
76
|
+
if m_lbl:
|
|
77
|
+
name, labels_str, _ = m_lbl.groups()
|
|
78
|
+
keys = [kv.split("=",1)[0] for kv in labels_str.split(",")]
|
|
79
|
+
else:
|
|
80
|
+
# unlabelled, maybe with stray {}: foo{} 42 or just foo 42
|
|
81
|
+
m_unlbl = re.match(r"^(\S+?)(?:\{\})?\s+(.+)", line)
|
|
82
|
+
if not m_unlbl:
|
|
83
|
+
continue
|
|
84
|
+
name, _ = m_unlbl.groups()
|
|
85
|
+
keys = []
|
|
86
|
+
|
|
87
|
+
label_map.setdefault(name, set()).update(keys)
|
|
88
|
+
|
|
89
|
+
# Propagate HELP/TYPE from base histograms/summaries
|
|
90
|
+
for series in list(label_map):
|
|
91
|
+
for suffix in ("_bucket", "_count", "_sum"):
|
|
92
|
+
if series.endswith(suffix):
|
|
93
|
+
base = series[:-len(suffix)]
|
|
94
|
+
if base in meta:
|
|
95
|
+
meta.setdefault(series, {}).update(meta[base])
|
|
96
|
+
break
|
|
97
|
+
|
|
98
|
+
# Merge into final metrics dict, with warnings
|
|
99
|
+
metrics = {}
|
|
100
|
+
all_names = set(meta) | set(label_map)
|
|
101
|
+
for name in sorted(all_names):
|
|
102
|
+
desc = meta.get(name, {}).get("description")
|
|
103
|
+
mtype = meta.get(name, {}).get("type")
|
|
104
|
+
unit = meta.get(name, {}).get("unit")
|
|
105
|
+
labels = sorted(label_map.get(name, []))
|
|
106
|
+
|
|
107
|
+
if desc is None:
|
|
108
|
+
logging.warning(f"Metric '{name}' has samples but no # HELP.")
|
|
109
|
+
desc = ""
|
|
110
|
+
if mtype is None:
|
|
111
|
+
logging.warning(f"Metric '{name}' has no # TYPE entry.")
|
|
112
|
+
|
|
113
|
+
metrics[name] = {
|
|
114
|
+
"description": desc,
|
|
115
|
+
"type": mtype,
|
|
116
|
+
"unit": unit,
|
|
117
|
+
"labels": labels
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
logging.info(f"Extracted {len(metrics)} metrics.")
|
|
121
|
+
return metrics
|
|
122
|
+
|
|
123
|
+
def output_asciidoc(metrics, adoc_file):
|
|
124
|
+
"""Output metrics as AsciiDoc."""
|
|
125
|
+
with open(adoc_file, "w") as f:
|
|
126
|
+
for name, data in metrics.items():
|
|
127
|
+
f.write(f"=== {name}\n\n")
|
|
128
|
+
f.write(f"{data['description']}\n\n")
|
|
129
|
+
f.write(f"*Type*: {data['type']}")
|
|
130
|
+
if data.get("unit"):
|
|
131
|
+
f.write(f"\n\n*Unit*: {data['unit']}")
|
|
132
|
+
if data["labels"]:
|
|
133
|
+
f.write("\n\n*Labels*:\n")
|
|
134
|
+
for label in data["labels"]:
|
|
135
|
+
f.write(f"\n- `{label}`")
|
|
136
|
+
f.write("\n\n---\n\n")
|
|
137
|
+
logging.info(f"AsciiDoc output written to {adoc_file}")
|
|
138
|
+
|
|
139
|
+
def output_json(metrics, json_file):
|
|
140
|
+
"""Output metrics as JSON."""
|
|
141
|
+
with open(json_file, "w") as f:
|
|
142
|
+
json.dump(metrics, f, indent=4)
|
|
143
|
+
logging.info(f"JSON output written to {json_file}")
|
|
144
|
+
|
|
145
|
+
def ensure_directory_exists(directory):
|
|
146
|
+
"""Ensure the given directory exists."""
|
|
147
|
+
if not os.path.exists(directory):
|
|
148
|
+
os.makedirs(directory)
|
|
149
|
+
|
|
150
|
+
if __name__ == "__main__":
|
|
151
|
+
# Expect the major.minor version to be provided as the first argument.
|
|
152
|
+
if len(sys.argv) < 2:
|
|
153
|
+
logging.error("Major.minor version must be provided as the first argument. Exiting.")
|
|
154
|
+
sys.exit(1)
|
|
155
|
+
|
|
156
|
+
tag_modified = sys.argv[1].strip()
|
|
157
|
+
|
|
158
|
+
# Resolve the base autogenerated folder at the repo root
|
|
159
|
+
repo_root = os.getcwd()
|
|
160
|
+
gen_path = os.path.join(repo_root, "autogenerated")
|
|
161
|
+
if not os.path.isdir(gen_path):
|
|
162
|
+
logging.error(f"autogenerated folder not found at: {gen_path}")
|
|
163
|
+
sys.exit(1)
|
|
164
|
+
|
|
165
|
+
# Build the output directory using the already provided tag_modified.
|
|
166
|
+
output_dir = os.path.join(gen_path, tag_modified, "metrics")
|
|
167
|
+
ensure_directory_exists(output_dir)
|
|
168
|
+
|
|
169
|
+
METRICS_URL = "http://localhost:19644/public_metrics/"
|
|
170
|
+
metrics_text = fetch_metrics(METRICS_URL)
|
|
171
|
+
if not metrics_text:
|
|
172
|
+
logging.error("No public metrics retrieved. Exiting.")
|
|
173
|
+
sys.exit(1)
|
|
174
|
+
|
|
175
|
+
public_metrics = parse_metrics(metrics_text)
|
|
176
|
+
|
|
177
|
+
# Fetch internal metrics if available.
|
|
178
|
+
INTERNAL_METRICS_URL = "http://localhost:19644/metrics/"
|
|
179
|
+
internal_metrics_text = fetch_metrics(INTERNAL_METRICS_URL)
|
|
180
|
+
if internal_metrics_text:
|
|
181
|
+
internal_metrics = parse_metrics(internal_metrics_text)
|
|
182
|
+
else:
|
|
183
|
+
logging.error("No internal metrics retrieved.")
|
|
184
|
+
internal_metrics = {}
|
|
185
|
+
|
|
186
|
+
# Merge public and internal metrics.
|
|
187
|
+
merged_metrics = {
|
|
188
|
+
"public": public_metrics,
|
|
189
|
+
"internal": internal_metrics
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# Define output file paths.
|
|
193
|
+
JSON_OUTPUT_FILE = os.path.join(output_dir, "metrics.json")
|
|
194
|
+
ASCIIDOC_OUTPUT_FILE = os.path.join(output_dir, "metrics.adoc")
|
|
195
|
+
INTERNAL_ASCIIDOC_OUTPUT_FILE = os.path.join(output_dir, "internal-metrics.adoc")
|
|
196
|
+
|
|
197
|
+
output_json(merged_metrics, JSON_OUTPUT_FILE)
|
|
198
|
+
output_asciidoc(public_metrics, ASCIIDOC_OUTPUT_FILE)
|
|
199
|
+
output_asciidoc(internal_metrics, INTERNAL_ASCIIDOC_OUTPUT_FILE)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests==2.32.3
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
.PHONY: build venv clean redpanda-git treesitter generate-docs check
|
|
2
|
+
|
|
3
|
+
# Default tag (can be overridden via `make TAG=v25.1.1`)
|
|
4
|
+
TAG ?= dev
|
|
5
|
+
|
|
6
|
+
# Derive a “major.minor” or rc identifier from TAG for folder naming
|
|
7
|
+
VERSION := $(shell \
|
|
8
|
+
if echo "$(TAG)" | grep -qE '^v?[0-9]+\.[0-9]+'; then \
|
|
9
|
+
echo "$(TAG)" \
|
|
10
|
+
| sed -E 's/^v?([0-9]+\.[0-9]+)(\.[0-9]+)?(-rc[0-9]+)?.*/\1\3/' \
|
|
11
|
+
| sed 's/-rc/rc/'; \
|
|
12
|
+
else \
|
|
13
|
+
echo "$(TAG)"; \
|
|
14
|
+
fi)
|
|
15
|
+
|
|
16
|
+
# Paths
|
|
17
|
+
REPO_ROOT := $(shell git rev-parse --show-toplevel)
|
|
18
|
+
MODULE_ROOT := $(shell cd "$(dir $(realpath $(lastword $(MAKEFILE_LIST))))"/../.. && pwd)
|
|
19
|
+
TOOL_ROOT := $(MODULE_ROOT)/tools/property-extractor
|
|
20
|
+
TMP_ROOT := $(TOOL_ROOT)/tmp
|
|
21
|
+
REDPANDA_SRC := $(TMP_ROOT)/redpanda
|
|
22
|
+
TREESITTER_DIR:= $(TOOL_ROOT)/tree-sitter/tree-sitter-cpp
|
|
23
|
+
VENV := $(TOOL_ROOT)/tmp/redpanda-property-extractor-venv
|
|
24
|
+
PYTHON := $(VENV)/bin/python
|
|
25
|
+
OUTPUT_DIR := $(REPO_ROOT)/autogenerated/$(TAG)/properties
|
|
26
|
+
TREE_SITTER := npx tree-sitter
|
|
27
|
+
|
|
28
|
+
# --- Main build: venv, fetch code, build parser, extract & docgen ---
|
|
29
|
+
build: venv redpanda-git treesitter
|
|
30
|
+
@echo "🔧 Building with Redpanda tag: $(TAG)"
|
|
31
|
+
@mkdir -p $(TOOL_ROOT)/gen
|
|
32
|
+
@cd $(TOOL_ROOT) && \
|
|
33
|
+
$(PYTHON) -W ignore::FutureWarning property_extractor.py \
|
|
34
|
+
--recursive \
|
|
35
|
+
--path $(REDPANDA_SRC) \
|
|
36
|
+
--output gen/properties-output.json
|
|
37
|
+
@echo "✅ JSON generated at $(TOOL_ROOT)/gen/properties-output.json"
|
|
38
|
+
@$(MAKE) generate-docs
|
|
39
|
+
|
|
40
|
+
# --- Ensure Python venv & dependencies ---
|
|
41
|
+
venv: $(TOOL_ROOT)/requirements.txt
|
|
42
|
+
@if [ ! -d "$(VENV)" ]; then \
|
|
43
|
+
echo "🐍 Creating virtual environment in $(VENV)..."; \
|
|
44
|
+
python3 -m venv $(VENV); \
|
|
45
|
+
$(VENV)/bin/pip install --upgrade pip --quiet; \
|
|
46
|
+
$(VENV)/bin/pip install --no-cache-dir -r $<; \
|
|
47
|
+
else \
|
|
48
|
+
echo "🐍 Virtual environment already exists at $(VENV)"; \
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# --- Clean out all generated state ---
|
|
52
|
+
clean:
|
|
53
|
+
@echo "🧹 Cleaning up…"
|
|
54
|
+
@rm -rf $(VENV) $(TMP_ROOT) $(REPO_ROOT)/autogenerated
|
|
55
|
+
|
|
56
|
+
# --- Clone or update Redpanda, checkout TAG or default branch ---
|
|
57
|
+
redpanda-git:
|
|
58
|
+
@mkdir -p "$(TMP_ROOT)"
|
|
59
|
+
@echo "🔄 Cloning/updating Redpanda into $(REDPANDA_SRC)…"
|
|
60
|
+
@if [ -d "$(REDPANDA_SRC)" ]; then \
|
|
61
|
+
git -C "$(REDPANDA_SRC)" fetch --all --tags -q; \
|
|
62
|
+
else \
|
|
63
|
+
git clone -q https://github.com/redpanda-data/redpanda.git "$(REDPANDA_SRC)"; \
|
|
64
|
+
fi; \
|
|
65
|
+
if git -C "$(REDPANDA_SRC)" rev-parse --verify -q "$(TAG)" >/dev/null; then \
|
|
66
|
+
echo "🔖 Checking out '$(TAG)'"; \
|
|
67
|
+
git -C "$(REDPANDA_SRC)" checkout -q "$(TAG)"; \
|
|
68
|
+
else \
|
|
69
|
+
DEFAULT_BRANCH=$$(git -C "$(REDPANDA_SRC)" symbolic-ref --short refs/remotes/origin/HEAD); \
|
|
70
|
+
echo "⚠️ Tag '$(TAG)' not found; falling back to '$${DEFAULT_BRANCH}'"; \
|
|
71
|
+
git -C "$(REDPANDA_SRC)" checkout -q "$${DEFAULT_BRANCH}"; \
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# --- Clone Tree-sitter grammar & generate parser ---
|
|
75
|
+
treesitter:
|
|
76
|
+
@echo "🌲 Ensuring tree-sitter-cpp grammar…"
|
|
77
|
+
@if [ ! -d "$(TREESITTER_DIR)" ]; then \
|
|
78
|
+
git clone https://github.com/tree-sitter/tree-sitter-cpp.git "$(TREESITTER_DIR)"; \
|
|
79
|
+
fi
|
|
80
|
+
@echo "🔧 Generating parser in $(TREESITTER_DIR)…"
|
|
81
|
+
@cd "$(TREESITTER_DIR)" && npm install --silent && $(TREE_SITTER) generate
|
|
82
|
+
|
|
83
|
+
# --- Turn the JSON into AsciiDoc pages under autogen/<version>/properties ---
|
|
84
|
+
generate-docs:
|
|
85
|
+
@echo "📝 Generating AsciiDoc pages in $(OUTPUT_DIR)…"
|
|
86
|
+
@mkdir -p "$(OUTPUT_DIR)"
|
|
87
|
+
@cd $(TOOL_ROOT) && \
|
|
88
|
+
$(PYTHON) json-to-asciidoc/generate_docs.py --output-dir "$(OUTPUT_DIR)"
|
|
89
|
+
@echo "✅ Docs generated at $(OUTPUT_DIR)"
|
|
90
|
+
|
|
91
|
+
# --- Debug helper to print all the key paths/vars ---
|
|
92
|
+
check:
|
|
93
|
+
@echo "MODULE_ROOT: $(MODULE_ROOT)"
|
|
94
|
+
@echo "TOOL_ROOT: $(TOOL_ROOT)"
|
|
95
|
+
@echo "REDPANDA_SRC: $(REDPANDA_SRC)"
|
|
96
|
+
@echo "TREESITTER: $(TREESITTER_DIR)"
|
|
97
|
+
@echo "VENV: $(VENV)"
|
|
98
|
+
@echo "PYTHON: $(PYTHON)"
|
|
99
|
+
@echo "OUTPUT_DIR: $(OUTPUT_DIR)"
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
= Redpanda Property Generator
|
|
2
|
+
|
|
3
|
+
The Redpanda Property Generator is a CLI tool designed to extract properties from Redpanda's source code and generate a JSON output with their definitions as well as Asciidoc pages.
|
|
4
|
+
|
|
5
|
+
== Prerequisites
|
|
6
|
+
|
|
7
|
+
Ensure the following prerequisites are installed on your system:
|
|
8
|
+
|
|
9
|
+
- https://www.python.org/downloads/[Python 3.10 or higher]
|
|
10
|
+
- A C++ compiler (such as `gcc`, `clang`)
|
|
11
|
+
- https://www.google.com/search?q=how+to+install+make[`make` utility] (to use the Makefile for automation)
|
|
12
|
+
+
|
|
13
|
+
To ensure `make` is available:
|
|
14
|
+
+
|
|
15
|
+
[,bash]
|
|
16
|
+
----
|
|
17
|
+
make --version
|
|
18
|
+
----
|
|
19
|
+
|
|
20
|
+
== Install
|
|
21
|
+
|
|
22
|
+
. Clone the repository:
|
|
23
|
+
+
|
|
24
|
+
[,bash]
|
|
25
|
+
----
|
|
26
|
+
git clone https://github.com/redpanda-data/docs-extensions-and-macros.git
|
|
27
|
+
cd docs-extensions-and-macros
|
|
28
|
+
----
|
|
29
|
+
|
|
30
|
+
== Generate properties
|
|
31
|
+
|
|
32
|
+
. Run the build process:
|
|
33
|
+
+
|
|
34
|
+
[,bash]
|
|
35
|
+
----
|
|
36
|
+
cd tools/property-extractor
|
|
37
|
+
make build
|
|
38
|
+
----
|
|
39
|
+
+
|
|
40
|
+
This command:
|
|
41
|
+
+
|
|
42
|
+
- Sets up the Python virtual environment (`venv`).
|
|
43
|
+
- Checks out the Redpanda source code to the specified branch or tag.
|
|
44
|
+
- Runs the extractor to generate a JSON file at `gen/properties-output.json`.
|
|
45
|
+
- Runs the docs generator to generate Asciidoc pages from the `properties-output.json`.
|
|
46
|
+
|
|
47
|
+
. Locate the generated files:
|
|
48
|
+
+
|
|
49
|
+
[,bash]
|
|
50
|
+
----
|
|
51
|
+
ls gen/properties-output.json
|
|
52
|
+
ls output
|
|
53
|
+
----
|
|
54
|
+
|
|
55
|
+
To clean the environment and generated files:
|
|
56
|
+
|
|
57
|
+
[,bash]
|
|
58
|
+
----
|
|
59
|
+
make clean
|
|
60
|
+
----
|
|
61
|
+
|
|
62
|
+
== Run the extractor manually
|
|
63
|
+
|
|
64
|
+
To run the extractor tool directly:
|
|
65
|
+
|
|
66
|
+
[,bash]
|
|
67
|
+
----
|
|
68
|
+
./property_extractor.py --path <path-to-redpanda-source> [options]
|
|
69
|
+
----
|
|
70
|
+
|
|
71
|
+
=== Command options
|
|
72
|
+
|
|
73
|
+
|===
|
|
74
|
+
| Option | Description
|
|
75
|
+
|
|
76
|
+
| `--path <path>`
|
|
77
|
+
| Path to the Redpanda source directory to extract properties from (required).
|
|
78
|
+
| `--recursive`
|
|
79
|
+
| Recursively scan the provided path for header (`*.h`) and implementation (`*.cc`) file pairs.
|
|
80
|
+
| `--output <output>`
|
|
81
|
+
| Path to the output JSON file. If not provided, the output will be printed to the console.
|
|
82
|
+
| `--definitions <definitions>`
|
|
83
|
+
| Path to the `definitions.json` file for type definitions (default: included `definitions.json`).
|
|
84
|
+
| `-v`, `--verbose`
|
|
85
|
+
| Enable verbose logging for debugging purposes.
|
|
86
|
+
|===
|
|
87
|
+
|
|
88
|
+
=== Example command
|
|
89
|
+
|
|
90
|
+
[,bash]
|
|
91
|
+
----
|
|
92
|
+
./property_extractor.py --path ./tmp/redpanda --recursive --output autogenerated/properties.json
|
|
93
|
+
----
|
|
94
|
+
|
|
95
|
+
=== How it works
|
|
96
|
+
|
|
97
|
+
. The tool identifies pairs of header (`*.h`) and implementation (`*.cc`) files in the specified Redpanda source directory. This ensures that both the declaration and definition of properties are available.
|
|
98
|
+
|
|
99
|
+
. Tree-sitter is used to parse the C{plus}{plus} source code and create abstract syntax trees (ASTs). Both the Tree-sitter C++ library (via a Git submodule) and its Python bindings (`tree_sitter`) are required for this step.
|
|
100
|
+
|
|
101
|
+
. Custom logic in `property_extractor.py` processes the ASTs to extract property definitions from specific files like:
|
|
102
|
+
+
|
|
103
|
+
- `src/v/config/configuration.cc`
|
|
104
|
+
- `src/v/kafka/client/configuration.cc`
|
|
105
|
+
|
|
106
|
+
. Extracted properties are processed by a series of transformers to enrich and normalize the data. For example:
|
|
107
|
+
+
|
|
108
|
+
- `BasicInfoTransformer`: Extracts names and metadata.
|
|
109
|
+
- `VisibilityTransformer`: Determines visibility (e.g., public or private).
|
|
110
|
+
- `IsNullableTransformer`: Detects if a property is nullable.
|
|
111
|
+
|
|
112
|
+
. The `definitions.json` file is merged into the output, linking property types to their descriptions.
|
|
113
|
+
|
|
114
|
+
=== JSON output
|
|
115
|
+
|
|
116
|
+
The final JSON contains:
|
|
117
|
+
|
|
118
|
+
- `properties`: Extracted properties with metadata.
|
|
119
|
+
- `definitions`: Type definitions, merged from `definitions.json`.
|
|
120
|
+
|
|
121
|
+
Example JSON structure:
|
|
122
|
+
|
|
123
|
+
[,json]
|
|
124
|
+
----
|
|
125
|
+
{
|
|
126
|
+
"properties": {
|
|
127
|
+
"example_property": {
|
|
128
|
+
"type": "string",
|
|
129
|
+
"description": "An example property."
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"definitions": {
|
|
133
|
+
"string": {
|
|
134
|
+
"description": "A string type."
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
----
|
|
139
|
+
|
|
140
|
+
=== Custom definitions
|
|
141
|
+
|
|
142
|
+
You can provide a custom `definitions.json` file:
|
|
143
|
+
|
|
144
|
+
[,bash]
|
|
145
|
+
----
|
|
146
|
+
./property_extractor.py --path ./tmp/redpanda --definitions custom-definitions.json --output autogenerated/custom-output.json
|
|
147
|
+
----
|
|
148
|
+
|
|
149
|
+
=== Debugging
|
|
150
|
+
|
|
151
|
+
Enable verbose logging to see detailed information:
|
|
152
|
+
|
|
153
|
+
[,bash]
|
|
154
|
+
----
|
|
155
|
+
./property_extractor.py --path ./tmp/redpanda --verbose
|
|
156
|
+
----
|
|
157
|
+
|
|
158
|
+
== Run the docs generator manually
|
|
159
|
+
|
|
160
|
+
. Make sure you have the `autogenerated/properties-output.json` file, relative to the `Makefile` location.
|
|
161
|
+
|
|
162
|
+
. Run the script:
|
|
163
|
+
+
|
|
164
|
+
[,bash]
|
|
165
|
+
----
|
|
166
|
+
python3 generate_docs.py
|
|
167
|
+
----
|
|
168
|
+
|
|
169
|
+
The script will process the JSON and generate AsciiDoc files in the `output/pages/` directory.
|
|
170
|
+
|
|
171
|
+
=== Output files
|
|
172
|
+
|
|
173
|
+
The following files will be generated:
|
|
174
|
+
|
|
175
|
+
- Broker Properties: `output/pages/broker-properties.adoc`
|
|
176
|
+
- Cluster Properties: `output/pages/cluster-properties.adoc`
|
|
177
|
+
- Object Storage Properties: `output/pages/object-storage-properties.adoc`
|
|
178
|
+
- Deprecated Properties: `output/pages/deprecated/partials/deprecated-properties.adoc`
|
|
179
|
+
|
|
180
|
+
=== Error reports
|
|
181
|
+
|
|
182
|
+
If the script encounters issues, it will generate error files in the `output/error/` directory:
|
|
183
|
+
|
|
184
|
+
- `empty_description.txt`: Properties without descriptions.
|
|
185
|
+
- `empty_type.txt`: Properties without types.
|
|
186
|
+
- `max_without_min.txt`: Properties with a maximum value but no minimum.
|
|
187
|
+
- `min_without_max.txt`: Properties with a minimum value but no maximum.
|
|
188
|
+
|
|
189
|
+
The console output will summarize the errors and property statistics.
|
|
190
|
+
|
|
191
|
+
=== How it works
|
|
192
|
+
|
|
193
|
+
. Input parsing:
|
|
194
|
+
- The script loads the JSON file from the `autogenerated/` directory.
|
|
195
|
+
- Properties are categorized into groups based on their `defined_in` field or specific naming conventions such as the `cloud_` prefix.
|
|
196
|
+
|
|
197
|
+
. Validation:
|
|
198
|
+
- Validates fields like `description`, `type`, `maximum`, and `minimum`.
|
|
199
|
+
- Identifies missing or inconsistent data and logs these to error files.
|
|
200
|
+
|
|
201
|
+
. Documentation generation:
|
|
202
|
+
- Creates AsciiDoc files with categorized properties, including metadata such as type, default value, visibility, and restart requirements.
|
|
203
|
+
- Appends appropriate titles, introductions, and formatting for each group.
|
|
204
|
+
|
|
205
|
+
. Error reporting: Generates error reports for easy debugging and correction of the input JSON.
|
|
206
|
+
|