|
| 1 | +#!/usr/bin/env python3 |
| 2 | +""" |
| 3 | +Generate comprehensive Redis commands reference pages for specific versions. |
| 4 | +Reads all command markdown files and creates a single page with collapsible sections. |
| 5 | +""" |
| 6 | + |
| 7 | +import os |
| 8 | +import re |
| 9 | +import sys |
| 10 | +from pathlib import Path |
| 11 | +from collections import defaultdict |
| 12 | +import yaml |
| 13 | + |
| 14 | +# Command group display names and order |
| 15 | +GROUP_ORDER = [ |
| 16 | + ('string', 'String Commands'), |
| 17 | + ('hash', 'Hash Commands'), |
| 18 | + ('list', 'List Commands'), |
| 19 | + ('set', 'Set Commands'), |
| 20 | + ('sorted-set', 'Sorted Set Commands'), |
| 21 | + ('stream', 'Stream Commands'), |
| 22 | + ('bitmap', 'Bitmap Commands'), |
| 23 | + ('hyperloglog', 'HyperLogLog Commands'), |
| 24 | + ('geo', 'Geospatial Commands'), |
| 25 | + ('json', 'JSON Commands'), |
| 26 | + ('search', 'Search Commands'), |
| 27 | + ('timeseries', 'Time Series Commands'), |
| 28 | + ('bloom', 'Probabilistic Commands'), |
| 29 | + ('vector_set', 'Vector Set Commands'), |
| 30 | + ('pubsub', 'Pub/Sub Commands'), |
| 31 | + ('transactions', 'Transaction Commands'), |
| 32 | + ('scripting', 'Scripting Commands'), |
| 33 | + ('connection', 'Connection Commands'), |
| 34 | + ('server', 'Server Commands'), |
| 35 | + ('cluster', 'Cluster Commands'), |
| 36 | + ('generic', 'Generic Commands'), |
| 37 | +] |
| 38 | + |
| 39 | +def parse_frontmatter(content): |
| 40 | + """Extract YAML frontmatter from markdown content.""" |
| 41 | + match = re.match(r'^---\s*\n(.*?)\n---\s*\n', content, re.DOTALL) |
| 42 | + if match: |
| 43 | + try: |
| 44 | + return yaml.safe_load(match.group(1)) |
| 45 | + except yaml.YAMLError: |
| 46 | + return {} |
| 47 | + return {} |
| 48 | + |
| 49 | +def get_command_files(): |
| 50 | + """Get all command markdown files.""" |
| 51 | + commands_dir = Path('content/commands') |
| 52 | + return list(commands_dir.glob('*.md')) |
| 53 | + |
| 54 | +def parse_command_file(filepath): |
| 55 | + """Parse a command file and extract relevant information.""" |
| 56 | + with open(filepath, 'r', encoding='utf-8') as f: |
| 57 | + content = f.read() |
| 58 | + |
| 59 | + frontmatter = parse_frontmatter(content) |
| 60 | + |
| 61 | + # Skip hidden commands and container commands |
| 62 | + if frontmatter.get('hidden', False): |
| 63 | + return None |
| 64 | + |
| 65 | + return { |
| 66 | + 'name': frontmatter.get('title', ''), |
| 67 | + 'link': frontmatter.get('linkTitle', ''), |
| 68 | + 'summary': frontmatter.get('summary', frontmatter.get('description', '')), |
| 69 | + 'syntax': frontmatter.get('syntax_fmt', ''), |
| 70 | + 'complexity': frontmatter.get('complexity', ''), |
| 71 | + 'since': frontmatter.get('since', ''), |
| 72 | + 'group': frontmatter.get('group', 'generic'), |
| 73 | + 'filepath': filepath.stem, |
| 74 | + } |
| 75 | + |
| 76 | +def version_compare(version_str): |
| 77 | + """Convert version string to tuple for comparison.""" |
| 78 | + if not version_str: |
| 79 | + return (0, 0, 0) |
| 80 | + # Handle versions like "8.4.0", "1.0.0", etc. |
| 81 | + parts = version_str.split('.') |
| 82 | + try: |
| 83 | + return tuple(int(p) for p in parts[:3]) |
| 84 | + except (ValueError, IndexError): |
| 85 | + return (0, 0, 0) |
| 86 | + |
| 87 | +def is_new_in_version(since_version, target_version): |
| 88 | + """Check if command was introduced in the target version.""" |
| 89 | + since_tuple = version_compare(since_version) |
| 90 | + target_tuple = version_compare(target_version) |
| 91 | + # Check if it's exactly the target version (major.minor match) |
| 92 | + return since_tuple[:2] == target_tuple[:2] |
| 93 | + |
| 94 | +def is_available_in_version(since_version, target_version): |
| 95 | + """Check if command is available in the target version (introduced in or before).""" |
| 96 | + return version_compare(since_version) <= version_compare(target_version) |
| 97 | + |
| 98 | +def generate_command_entry(cmd, target_version): |
| 99 | + """Generate HTML for a single command entry.""" |
| 100 | + is_new = is_new_in_version(cmd['since'], target_version) |
| 101 | + version_display = target_version.rsplit('.', 1)[0] # "8.4.0" -> "8.4" |
| 102 | + new_badge = f' <span style="color: #e74c3c;">⭐ New in {version_display}</span>' if is_new else '' |
| 103 | + |
| 104 | + # Clean up syntax for display |
| 105 | + syntax = cmd['syntax'].replace('_', ' ').replace('\\n', ' ') |
| 106 | + |
| 107 | + entry = f'''<details> |
| 108 | +<summary><strong><a href="/commands/{cmd['filepath']}/">{cmd['name']}</a></strong> - {cmd['summary']}{new_badge}</summary> |
| 109 | +
|
| 110 | +**Syntax:** `{syntax}` |
| 111 | +
|
| 112 | +**Description:** {cmd['summary']} |
| 113 | +
|
| 114 | +**Complexity:** {cmd['complexity']} |
| 115 | +
|
| 116 | +**Since:** {cmd['since']} |
| 117 | +
|
| 118 | +</details> |
| 119 | +
|
| 120 | +''' |
| 121 | + return entry |
| 122 | + |
| 123 | +def generate_page_content(commands_by_group, target_version): |
| 124 | + """Generate the complete page content.""" |
| 125 | + content = [] |
| 126 | + |
| 127 | + version_display = target_version.rsplit('.', 1)[0] # "8.4.0" -> "8.4" |
| 128 | + |
| 129 | + # Header |
| 130 | + content.append(f'''--- |
| 131 | +title: Redis {version_display} Commands Reference |
| 132 | +linkTitle: Redis {version_display} Commands |
| 133 | +description: Complete list of all Redis commands available in version {version_display}, organized by functional group |
| 134 | +layout: single |
| 135 | +type: develop |
| 136 | +categories: |
| 137 | +- docs |
| 138 | +- develop |
| 139 | +- stack |
| 140 | +- oss |
| 141 | +- rs |
| 142 | +- rc |
| 143 | +- kubernetes |
| 144 | +- clients |
| 145 | +weight: 1 |
| 146 | +--- |
| 147 | +
|
| 148 | +This page provides a comprehensive reference of all Redis commands available in Redis {version_display}, organized by functional group. Each command includes its description and syntax in a collapsible section for easy navigation. |
| 149 | +
|
| 150 | +{{{{< note >}}}} |
| 151 | +Redis {version_display} includes all commands from previous versions plus new commands introduced in {version_display}. Commands marked with **⭐ New in {version_display}** were added in this release. |
| 152 | +{{{{< /note >}}}} |
| 153 | +
|
| 154 | +## Quick Navigation |
| 155 | +
|
| 156 | +''') |
| 157 | + |
| 158 | + # Table of contents |
| 159 | + for group_key, group_name in GROUP_ORDER: |
| 160 | + if group_key in commands_by_group: |
| 161 | + anchor = group_name.lower().replace(' ', '-').replace('/', '') |
| 162 | + content.append(f'- [{group_name}](#{anchor})\n') |
| 163 | + |
| 164 | + content.append('\n---\n\n') |
| 165 | + |
| 166 | + # Generate sections for each group |
| 167 | + for group_key, group_name in GROUP_ORDER: |
| 168 | + if group_key not in commands_by_group: |
| 169 | + continue |
| 170 | + |
| 171 | + commands = sorted(commands_by_group[group_key], key=lambda x: x['name']) |
| 172 | + |
| 173 | + # Section header |
| 174 | + anchor = group_name.lower().replace(' ', '-').replace('/', '') |
| 175 | + content.append(f'## {group_name}\n\n') |
| 176 | + |
| 177 | + # Group description |
| 178 | + group_descriptions = { |
| 179 | + 'string': 'String commands operate on string values, the most basic Redis data type.', |
| 180 | + 'hash': 'Hash commands operate on hash data structures, which map string fields to string values.', |
| 181 | + 'list': 'List commands operate on lists of strings, ordered by insertion order.', |
| 182 | + 'set': 'Set commands operate on unordered collections of unique strings.', |
| 183 | + 'sorted-set': 'Sorted set commands operate on sets of unique strings ordered by a score.', |
| 184 | + 'stream': 'Stream commands operate on append-only log data structures.', |
| 185 | + 'bitmap': 'Bitmap commands operate on strings as arrays of bits.', |
| 186 | + 'hyperloglog': 'HyperLogLog commands provide probabilistic cardinality estimation.', |
| 187 | + 'geo': 'Geospatial commands operate on geographic coordinates.', |
| 188 | + 'json': 'JSON commands operate on JSON data structures.', |
| 189 | + 'search': 'Search commands provide full-text search and secondary indexing.', |
| 190 | + 'timeseries': 'Time series commands operate on time-series data.', |
| 191 | + 'bloom': 'Probabilistic data structure commands (Bloom filters, Cuckoo filters, etc.).', |
| 192 | + 'vector_set': 'Vector set commands operate on vector data structures for similarity search and range queries.', |
| 193 | + 'pubsub': 'Pub/Sub commands enable message passing between clients.', |
| 194 | + 'transactions': 'Transaction commands enable atomic execution of command groups.', |
| 195 | + 'scripting': 'Scripting commands enable server-side Lua script execution.', |
| 196 | + 'connection': 'Connection commands manage client connections.', |
| 197 | + 'server': 'Server commands provide server management and introspection.', |
| 198 | + 'cluster': 'Cluster commands manage Redis Cluster operations.', |
| 199 | + 'generic': 'Generic commands work across all data types.', |
| 200 | + } |
| 201 | + |
| 202 | + if group_key in group_descriptions: |
| 203 | + content.append(f'{group_descriptions[group_key]}\n\n') |
| 204 | + |
| 205 | + # Add commands |
| 206 | + for cmd in commands: |
| 207 | + content.append(generate_command_entry(cmd, target_version)) |
| 208 | + |
| 209 | + content.append('\n') |
| 210 | + |
| 211 | + return ''.join(content) |
| 212 | + |
| 213 | +def main(): |
| 214 | + """Main function to generate the commands page.""" |
| 215 | + # Get target version from command line argument or default to 8.4.0 |
| 216 | + if len(sys.argv) > 1: |
| 217 | + target_version = sys.argv[1] |
| 218 | + if not target_version.count('.') == 2: |
| 219 | + target_version = f"{target_version}.0" |
| 220 | + else: |
| 221 | + target_version = "8.4.0" |
| 222 | + |
| 223 | + version_display = target_version.rsplit('.', 1)[0] # "8.4.0" -> "8.4" |
| 224 | + |
| 225 | + print(f"Generating Redis {version_display} commands reference page...") |
| 226 | + print("Parsing command files...") |
| 227 | + |
| 228 | + commands_by_group = defaultdict(list) |
| 229 | + total_commands = 0 |
| 230 | + new_commands = 0 |
| 231 | + |
| 232 | + for filepath in get_command_files(): |
| 233 | + cmd_data = parse_command_file(filepath) |
| 234 | + if cmd_data and is_available_in_version(cmd_data['since'], target_version): |
| 235 | + commands_by_group[cmd_data['group']].append(cmd_data) |
| 236 | + total_commands += 1 |
| 237 | + if is_new_in_version(cmd_data['since'], target_version): |
| 238 | + new_commands += 1 |
| 239 | + |
| 240 | + print(f"Found {total_commands} commands ({new_commands} new in {version_display})") |
| 241 | + print(f"Organized into {len(commands_by_group)} groups") |
| 242 | + |
| 243 | + # Generate page content |
| 244 | + print("Generating page content...") |
| 245 | + content = generate_page_content(commands_by_group, target_version) |
| 246 | + |
| 247 | + # Write to file |
| 248 | + output_file = Path(f'content/commands/redis-{version_display.replace(".", "-")}-commands.md') |
| 249 | + with open(output_file, 'w', encoding='utf-8') as f: |
| 250 | + f.write(content) |
| 251 | + |
| 252 | + print(f"✅ Generated {output_file}") |
| 253 | + print(f" Total commands: {total_commands}") |
| 254 | + print(f" New in {version_display}: {new_commands}") |
| 255 | + |
| 256 | +if __name__ == '__main__': |
| 257 | + main() |
| 258 | + |
0 commit comments