diff --git a/src/current/Gemfile b/src/current/Gemfile
index baf71339668..b3b82646f0d 100644
--- a/src/current/Gemfile
+++ b/src/current/Gemfile
@@ -12,7 +12,11 @@ gem "redcarpet", "~> 3.6"
gem "rss"
gem "webrick"
gem "jekyll-minifier"
-
+gem 'csv'
+gem 'logger'
+gem 'base64'
+gem 'bigdecimal'
+gem 'mutex_m'
group :jekyll_plugins do
gem "jekyll-include-cache"
gem 'jekyll-algolia', "~> 1.0", path: "./jekyll-algolia-dev"
diff --git a/src/current/_config_cockroachdb_local.yml b/src/current/_config_cockroachdb_local.yml
index 3440c9a8df7..98579dde61f 100644
--- a/src/current/_config_cockroachdb_local.yml
+++ b/src/current/_config_cockroachdb_local.yml
@@ -4,7 +4,6 @@ exclude:
- "v2.0"
- "v2.1"
- "v19.1"
-- "v19.2"
- "v20.1"
- "ci"
- "scripts"
diff --git a/src/current/audit.py b/src/current/audit.py
new file mode 100644
index 00000000000..2d2c968735d
--- /dev/null
+++ b/src/current/audit.py
@@ -0,0 +1,408 @@
+#!/usr/bin/env python3
+"""
+audit.py
+
+An audit script that:
+1) Finds cross-version links (categorized by location).
+2) Finds cockroachlabs.com non-docs links.
+3) Finds external (non-cockroachlabs.com) links.
+4) Audits image/CSS/JS/font usage, categorizing them as present, missing, or external.
+
+**This version** uses a "fallback" approach in asset_status() so
+we do *not* unconditionally remove "/docs/" from the path. Instead,
+we generate multiple candidate paths and see if any match the disk.
+"""
+
+import os
+import sys
+import re
+import argparse
+from bs4 import BeautifulSoup
+from urllib.parse import urlparse
+
+def is_cross_version_link(url, current_version):
+ """
+ Return (True, found_version) if `url` is a docs link pointing to a different version.
+ E.g. /docs/v19.2/... vs current_version v21.1
+ """
+ match = re.search(r'/docs/(v\d+\.\d+)', url)
+ if match:
+ version = match.group(1)
+ return (version != current_version, version)
+ return (False, None)
+
+def categorize_cross_version_link(tag):
+ """
+ For cross-version links, figure out if they're in the sidebar, version-switcher, or body.
+ """
+ if tag.find_parent(id="sidebar"):
+ return "Sidebar Navigation"
+ elif tag.find_parent(id="version-switcher"):
+ return "Version Switcher"
+ else:
+ return "Content Body"
+
+def find_assets(soup):
+ """
+ Return a dict: { "images": set(), "css": set(), "js": set(), "fonts": set() }
+ by scanning
, ,
+
+'''
+
+ html = re.sub(r"", nav_deps + "\n", html, flags=re.IGNORECASE)
+
+ # Add offline styles
+ offline_styles = f''''''
+
+ html = re.sub(r"", offline_styles + "\n", html, flags=re.IGNORECASE)
+
+ # Add navigation initialization
+ nav_init = """"""
+
+ html = re.sub(r"
+
+
+
+ 📱
+ Offline Documentation Archive - CockroachDB Version 19.2
+
+
+
+
+
+
+
+
Documentation
+
CockroachDB is the SQL database for building global, scalable cloud services that survive disasters.
+
+
+
+
+
☁️
+
Start a cloud cluster
+
Get started with CockroachDB Cloud, our fully managed service.
+
+ Learn more →
+
+
+
+
+
🖥️
+
Start a local cluster
+
Set up a local CockroachDB cluster for development and testing.
+
+ Learn more →
+
+
+
+
+
🚀
+
Build a sample app
+
Build applications using your favorite language and framework.
+
+ Learn more →
+
+
+
+
+
+
+
+
+
+
+
+
+", nav_init + "\n", html, flags=re.IGNORECASE)
+
+ # Write output
+ dst_path.parent.mkdir(parents=True, exist_ok=True)
+ dst_path.write_text(html, encoding="utf-8")
+
+ self.processed_files.add(str(rel_path))
+
+ except Exception as e:
+ self.log(f"Error processing {src_path}: {e}", "ERROR")
+ import traceback
+ traceback.print_exc()
+
+ def fix_css_images(self):
+ """Fix image paths in CSS files"""
+ self.log("Fixing CSS image paths...")
+
+ for css_file in (OUTPUT_ROOT / "css").rglob("*.css"):
+ try:
+ content = css_file.read_text(encoding="utf-8")
+
+ # Fix various image URL patterns
+ content = re.sub(
+ r"url\((['\"]?)/?docs/images/([^)\"']+)\1\)",
+ r"url(\1../images/\2\1)",
+ content,
+ )
+ content = re.sub(
+ r"url\((['\"]?)images/([^)\"']+)\1\)",
+ r"url(\1../images/\2\1)",
+ content,
+ )
+
+ css_file.write_text(content, encoding="utf-8")
+
+ except Exception as e:
+ self.log(f"Error fixing CSS {css_file}: {e}", "WARNING")
+
+ def download_google_fonts(self):
+ """Download and localize Google Fonts"""
+ self.log("Downloading Google Fonts...")
+
+ fonts_dir = OUTPUT_ROOT / "fonts"
+ fonts_dir.mkdir(exist_ok=True)
+
+ try:
+ headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}
+ css_response = requests.get(FONTS_CSS_URL, headers=headers, timeout=10)
+ css_response.raise_for_status()
+ css_content = css_response.text
+
+ font_urls = set(re.findall(r"url\((https://fonts\.gstatic\.com/[^\)]+)\)", css_content))
+
+ for url in font_urls:
+ try:
+ font_response = requests.get(url, headers=headers, timeout=10)
+ font_response.raise_for_status()
+
+ parsed = urlparse(url)
+ font_path = parsed.path.lstrip("/")
+ dst = fonts_dir / font_path
+ dst.parent.mkdir(parents=True, exist_ok=True)
+ dst.write_bytes(font_response.content)
+
+ css_content = css_content.replace(url, f"../fonts/{font_path}")
+
+ except Exception as e:
+ self.log(f"Failed to download font from {url}: {e}", "WARNING")
+
+ (OUTPUT_ROOT / "css" / "google-fonts.css").write_text(css_content, encoding="utf-8")
+ self.log("Google Fonts localized", "SUCCESS")
+
+ except Exception as e:
+ self.log(f"Error downloading fonts: {e}", "ERROR")
+ fallback = """/* Fallback fonts */
+body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif; }
+code, pre { font-family: Consolas, Monaco, "Courier New", monospace; }"""
+ (OUTPUT_ROOT / "css" / "google-fonts.css").write_text(fallback)
+
+ def create_index_page(self):
+ """Create the index page with proper CockroachDB purple branding"""
+ index_html = f"""
+
+
+
+
+
CockroachDB Documentation
+
+
+
+
+
+
+