#!/usr/bin/env python3 """ Dashy Documentation Updater for Docusaurus =========================================== This script fetches the latest documentation from the Dashy master branch and applies all necessary fixes to make it compatible with Docusaurus. Usage: python3 update-docs.py """ import os import re import glob import subprocess import sys import shutil from pathlib import Path def run_command(cmd, description): """Run a shell command and return success status""" print(f"๐Ÿ“‹ {description}...") try: result = subprocess.run(cmd, shell=True, check=True, capture_output=True, text=True) print(f"โœ… {description} - SUCCESS") return True except subprocess.CalledProcessError as e: print(f"โŒ {description} - FAILED") print(f"Error: {e.stderr}") return False def download_docs(): """Download latest docs from master branch""" print("๐Ÿš€ Starting Dashy documentation update...") # Create temporary directory if os.path.exists('temp_docs'): shutil.rmtree('temp_docs') # Download docs using git archive cmd = 'git archive origin/master docs/ | tar -x -C temp_docs --strip-components=0' if not run_command('mkdir -p temp_docs', 'Create temporary directory'): return False if not run_command('git fetch origin master', 'Fetch latest master branch'): return False if not run_command('git archive origin/master docs/ | tar -x -C temp_docs', 'Download docs from master'): return False # Copy to docs directory if not run_command('cp -r temp_docs/docs/* docs/', 'Copy docs to current directory'): return False # Clean up shutil.rmtree('temp_docs', ignore_errors=True) return True def fix_markdown_file(filepath): """Apply all Docusaurus compatibility fixes to a single markdown file""" print(f"๐Ÿ”ง Processing: {filepath}") try: with open(filepath, 'r', encoding='utf-8') as f: content = f.read() except Exception as e: print(f"โŒ Error reading {filepath}: {e}") return False original_content = content # 1. Remove HTML comments content = re.sub(r'', '', content, flags=re.DOTALL) # 2. Replace
with
content = re.sub(r'', '
', content) content = re.sub(r'', '
', content) # 3. Fix unterminated

tags (the main JSX issue we found) # Remove empty

tags content = re.sub(r'

\s*\n\s*

', '', content) content = re.sub(r']*>\s*\n\s*([^<\n]*)\n\s*

', r'\1', content) # Fix specific unterminated

issue in crypto donation section content = re.sub(r'

\s*\n\s*(\[!\[.*?\].*?\].*?\n)', r'\1', content) # Remove problematic SVG charts that cause JSX errors content = re.sub(r'\[!\[Stargazers\].*?starchart\.cc.*?\)', '', content, flags=re.DOTALL) content = re.sub(r'\[!\[Contributors\].*?contrib\.rocks.*?\)', '', content, flags=re.DOTALL) content = re.sub(r'\[!\[Auto-generated contributors\].*?CONTRIBUTORS\.svg.*?\)', '', content, flags=re.DOTALL) # 4. Remove .md extension from links content = re.sub(r'\[([^\]]+)\]\(([^)]+)\.md\)', r'[\1](\2)', content) # 5. Make links relative - replace ./docs with /docs content = re.sub(r'\]\(\./docs/', '](/docs/', content) content = re.sub(r'\]\(\.\./docs/', '](/docs/', content) # 6. Fix docs/file.md to /docs/file content = re.sub(r'\]\(docs/([^)]+)\.md\)', r'](/docs/\1)', content) # 7. Fix any remaining .md links and /docs/ prefixes content = re.sub(r'(/docs/[^)]+)\.md\)', r'\1)', content) content = re.sub(r'(docs/[^)]+)\.md\)', r'/\1)', content) # 8. Critical fix: Convert /docs/ links to relative paths for Docusaurus # This was the key issue we discovered during testing content = re.sub(r'\(/docs/([^)]*)\)', r'(/\1)', content) # 9. Replace back to top buttons content = re.sub(r'\[โฌ†๏ธ Back to Top\]\(#[^)]*\)', '**[โฌ†๏ธ Back to Top](#)**', content) content = re.sub(r'

\s*โฌ†๏ธ Back to Top\s*

', '**[โฌ†๏ธ Back to Top](#)**', content) # 10. Remove
tags and convert to headings content = re.sub(r']*>\s*([^<]+)\s*', r'### \1\n\n', content) content = re.sub(r'
', '', content) # 11. Fix GitHub blob links to relative links content = re.sub(r'https://github\.com/[^/]+/[^/]+/blob/[^/]+/docs/([^)]+)\.md', r'/docs/\1', content) content = re.sub(r'https://github\.com/[^/]+/[^/]+/blob/[^/]+/docs/([^)]+)', r'/docs/\1', content) # 12. Ensure each file has only one main heading main_headings = re.findall(r'^# .+', content, re.MULTILINE) if len(main_headings) > 1: lines = content.split('\n') first_main_heading_found = False for i, line in enumerate(lines): if re.match(r'^# .+', line): if first_main_heading_found: lines[i] = '#' + line # Convert # to ## else: first_main_heading_found = True content = '\n'.join(lines) # 12. Fix self-closing HTML tags content = re.sub(r']*[^/])>', r'', content) content = re.sub(r'', '
', content) content = re.sub(r'', '
', content) # 13. Clean up extra whitespace content = re.sub(r'\n\n\n+', '\n\n', content) content = content.strip() # 14. Ensure file ends with newline if not content.endswith('\n'): content += '\n' # 15. Special fixes for known problematic files if 'contributing.md' in filepath: # Remove problematic SVG contributors chart that causes JSX issues content = re.sub(r'\[\!\[Auto-generated contributors\].*?\]\([^)]+\)', '', content, flags=re.DOTALL) # Remove star-gazers chart content = re.sub(r'### Star-Gazers Over Time.*?$', '', content, flags=re.DOTALL | re.MULTILINE) # Fix unterminated

tag issue content = re.sub(r'- \*\*ZEC\*\*: `[^`]+`\s*\n\s*

', lambda m: m.group(0).replace('

', ''), content) # Only write if content changed if content != original_content: try: with open(filepath, 'w', encoding='utf-8') as f: f.write(content) print(f"โœ… Fixed: {filepath}") return True except Exception as e: print(f"โŒ Error writing {filepath}: {e}") return False else: print(f"โ„น๏ธ No changes needed: {filepath}") return True def process_all_docs(): """Process all markdown files in the docs directory""" docs_dir = 'docs' if not os.path.exists(docs_dir): print(f"โŒ Error: {docs_dir} directory not found!") return False # Find all .md files recursively md_files = glob.glob(os.path.join(docs_dir, '**', '*.md'), recursive=True) if not md_files: print(f"โŒ No markdown files found in {docs_dir}") return False print(f"๐Ÿ“š Found {len(md_files)} markdown files to process...") success_count = 0 for md_file in md_files: if fix_markdown_file(md_file): success_count += 1 else: print(f"โŒ Failed to process {md_file}") print(f"\n๐ŸŽ‰ Successfully processed {success_count}/{len(md_files)} files!") return success_count == len(md_files) def test_build(): """Test the Docusaurus build""" print("๐Ÿงช Testing Docusaurus build...") # Kill any running development servers run_command("pkill -f 'docusaurus start' || true", "Stop any running dev servers") # Test the build if run_command("npm run build", "Build Docusaurus site"): print("โœ… Build successful! All documentation is now Docusaurus compatible.") return True else: print("โŒ Build failed. There may be remaining compatibility issues.") return False def main(): """Main function to orchestrate the documentation update""" print("=" * 60) print("๐Ÿš€ Dashy Documentation Updater for Docusaurus") print("=" * 60) # Check if we're in the right directory if not os.path.exists('package.json'): print("โŒ Error: package.json not found. Please run this script from the Docusaurus project root.") sys.exit(1) if not os.path.exists('docusaurus.config.js'): print("โŒ Error: docusaurus.config.js not found. Please run this script from the Docusaurus project root.") sys.exit(1) steps = [ ("๐Ÿ“ฅ Download latest docs", download_docs), ("๐Ÿ”ง Process all markdown files", process_all_docs), ("๐Ÿงช Test build", test_build) ] for step_name, step_func in steps: print(f"\n{step_name}") print("-" * 40) if not step_func(): print(f"\nโŒ FAILED: {step_name}") print("Please check the errors above and try again.") sys.exit(1) print(f"โœ… COMPLETED: {step_name}") print("\n" + "=" * 60) print("๐ŸŽ‰ SUCCESS: Documentation update completed successfully!") print("=" * 60) print("\n๐Ÿ“‹ Summary:") print("โ€ข Downloaded latest docs from master branch") print("โ€ข Fixed all Docusaurus compatibility issues") print("โ€ข Verified build works correctly") print("\n๐Ÿš€ You can now run 'yarn start' to see your updated docs!") if __name__ == "__main__": main()