#!/bin/bash # Rustelo Documentation Build Script # This script builds the documentation using mdBook, cargo doc, and organizes the output set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")" echo -e "${BLUE}🚀 Rustelo Documentation Build Script${NC}" echo "=================================" # Check if mdbook is installed if ! command -v mdbook &> /dev/null; then echo -e "${RED}❌ mdbook is not installed${NC}" echo "Please install mdbook:" echo " cargo install mdbook" echo " # Optional plugins:" echo " cargo install mdbook-linkcheck" echo " cargo install mdbook-toc" echo " cargo install mdbook-mermaid" exit 1 fi # Check mdbook version MDBOOK_VERSION=$(mdbook --version | cut -d' ' -f2) echo -e "${GREEN}✅ mdbook version: $MDBOOK_VERSION${NC}" # Create necessary directories echo -e "${BLUE}📁 Creating directories...${NC}" mkdir -p "$PROJECT_ROOT/book-output" mkdir -p "$PROJECT_ROOT/book/theme" # Copy custom theme files if they don't exist if [ ! -f "$PROJECT_ROOT/book/theme/custom.css" ]; then echo -e "${YELLOW}📝 Creating custom CSS...${NC}" cat > "$PROJECT_ROOT/book/theme/custom.css" << 'EOF' /* Rustelo Documentation Custom Styles */ :root { --rustelo-primary: #e53e3e; --rustelo-secondary: #3182ce; --rustelo-accent: #38a169; --rustelo-dark: #2d3748; --rustelo-light: #f7fafc; } /* Custom header styling */ .menu-title { color: var(--rustelo-primary); font-weight: bold; } /* Code block improvements */ pre { border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* Improved table styling */ table { border-collapse: collapse; width: 100%; margin: 1rem 0; } table th, table td { border: 1px solid #e2e8f0; padding: 0.75rem; text-align: left; } table th { background-color: var(--rustelo-light); font-weight: 600; } table tr:nth-child(even) { background-color: #f8f9fa; } /* Feature badge styling */ .feature-badge { display: inline-block; padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-size: 0.875rem; font-weight: 500; margin: 0.125rem; } .feature-badge.enabled { background-color: #c6f6d5; color: #22543d; } .feature-badge.disabled { background-color: #fed7d7; color: #742a2a; } .feature-badge.optional { background-color: #fef5e7; color: #744210; } /* Callout boxes */ .callout { padding: 1rem; margin: 1rem 0; border-left: 4px solid; border-radius: 0 4px 4px 0; } .callout.note { border-left-color: var(--rustelo-secondary); background-color: #ebf8ff; } .callout.warning { border-left-color: #ed8936; background-color: #fffaf0; } .callout.tip { border-left-color: var(--rustelo-accent); background-color: #f0fff4; } .callout.danger { border-left-color: var(--rustelo-primary); background-color: #fff5f5; } /* Command line styling */ .command-line { background-color: #1a202c; color: #e2e8f0; padding: 1rem; border-radius: 8px; font-family: 'JetBrains Mono', 'Fira Code', monospace; margin: 1rem 0; } .command-line::before { content: "$ "; color: #48bb78; font-weight: bold; } /* Navigation improvements */ .chapter li.part-title { color: var(--rustelo-primary); font-weight: bold; margin-top: 1rem; } /* Search improvements */ #searchresults mark { background-color: #fef5e7; color: #744210; } /* Mobile improvements */ @media (max-width: 768px) { .content { padding: 1rem; } table { font-size: 0.875rem; } .command-line { font-size: 0.8rem; padding: 0.75rem; } } /* Dark theme overrides */ .navy .callout.note { background-color: #1e3a8a; } .navy .callout.warning { background-color: #92400e; } .navy .callout.tip { background-color: #14532d; } .navy .callout.danger { background-color: #991b1b; } /* Print styles */ @media print { .nav-wrapper, .page-wrapper > .page > .menu, .mobile-nav-chapters, .nav-chapters, .sidebar-scrollbox { display: none !important; } .page-wrapper > .page { left: 0 !important; } .content { margin-left: 0 !important; max-width: none !important; } } EOF fi if [ ! -f "$PROJECT_ROOT/book/theme/custom.js" ]; then echo -e "${YELLOW}📝 Creating custom JavaScript...${NC}" cat > "$PROJECT_ROOT/book/theme/custom.js" << 'EOF' // Rustelo Documentation Custom JavaScript // Add copy buttons to code blocks document.addEventListener('DOMContentLoaded', function() { // Add copy buttons to code blocks const codeBlocks = document.querySelectorAll('pre > code'); codeBlocks.forEach(function(codeBlock) { const pre = codeBlock.parentElement; const button = document.createElement('button'); button.className = 'copy-button'; button.textContent = 'Copy'; button.style.cssText = ` position: absolute; top: 8px; right: 8px; background: #4a5568; color: white; border: none; padding: 4px 8px; border-radius: 4px; font-size: 12px; cursor: pointer; opacity: 0; transition: opacity 0.2s; `; pre.style.position = 'relative'; pre.appendChild(button); pre.addEventListener('mouseenter', function() { button.style.opacity = '1'; }); pre.addEventListener('mouseleave', function() { button.style.opacity = '0'; }); button.addEventListener('click', function() { const text = codeBlock.textContent; navigator.clipboard.writeText(text).then(function() { button.textContent = 'Copied!'; button.style.background = '#48bb78'; setTimeout(function() { button.textContent = 'Copy'; button.style.background = '#4a5568'; }, 2000); }); }); }); // Add feature badges const content = document.querySelector('.content'); if (content) { let html = content.innerHTML; // Replace feature indicators html = html.replace(/\[FEATURE:([^\]]+)\]/g, '$1'); html = html.replace(/\[OPTIONAL:([^\]]+)\]/g, '$1'); html = html.replace(/\[DISABLED:([^\]]+)\]/g, '$1'); // Add callout boxes html = html.replace(/\[NOTE\]([\s\S]*?)\[\/NOTE\]/g, '
$1
'); html = html.replace(/\[WARNING\]([\s\S]*?)\[\/WARNING\]/g, '
$1
'); html = html.replace(/\[TIP\]([\s\S]*?)\[\/TIP\]/g, '
$1
'); html = html.replace(/\[DANGER\]([\s\S]*?)\[\/DANGER\]/g, '
$1
'); content.innerHTML = html; } // Add smooth scrolling document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth' }); } }); }); }); // Add keyboard shortcuts document.addEventListener('keydown', function(e) { // Ctrl/Cmd + K to focus search if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault(); const searchInput = document.querySelector('#searchbar'); if (searchInput) { searchInput.focus(); } } }); // Add version info to footer document.addEventListener('DOMContentLoaded', function() { const content = document.querySelector('.content'); if (content) { const footer = document.createElement('div'); footer.style.cssText = ` margin-top: 3rem; padding: 2rem 0; border-top: 1px solid #e2e8f0; text-align: center; font-size: 0.875rem; color: #718096; `; footer.innerHTML = `

Built with ❤️ using mdBook

Rustelo Documentation • Last updated: ${new Date().toLocaleDateString()}

`; content.appendChild(footer); } }); EOF fi # Check if we should sync content from existing docs if [ "$1" = "--sync" ]; then echo -e "${BLUE}🔄 Syncing content from existing documentation...${NC}" # Create directories for existing content mkdir -p "$PROJECT_ROOT/book/database" mkdir -p "$PROJECT_ROOT/book/features/auth" mkdir -p "$PROJECT_ROOT/book/features/content" # Copy and adapt existing documentation if [ -f "$PROJECT_ROOT/docs/database_configuration.md" ]; then cp "$PROJECT_ROOT/docs/database_configuration.md" "$PROJECT_ROOT/book/database/configuration.md" echo -e "${GREEN}✅ Synced database configuration${NC}" fi if [ -f "$PROJECT_ROOT/docs/2fa_implementation.md" ]; then cp "$PROJECT_ROOT/docs/2fa_implementation.md" "$PROJECT_ROOT/book/features/auth/2fa.md" echo -e "${GREEN}✅ Synced 2FA documentation${NC}" fi if [ -f "$PROJECT_ROOT/docs/email.md" ]; then cp "$PROJECT_ROOT/docs/email.md" "$PROJECT_ROOT/book/features/email.md" echo -e "${GREEN}✅ Synced email documentation${NC}" fi # Copy from info directory if [ -f "$PROJECT_ROOT/info/features.md" ]; then cp "$PROJECT_ROOT/info/features.md" "$PROJECT_ROOT/book/features/detailed.md" echo -e "${GREEN}✅ Synced detailed features${NC}" fi echo -e "${GREEN}✅ Content sync complete${NC}" fi # Change to project root cd "$PROJECT_ROOT" # Build the documentation echo -e "${BLUE}🔨 Building documentation...${NC}" if mdbook build; then echo -e "${GREEN}✅ Documentation built successfully${NC}" else echo -e "${RED}❌ Documentation build failed${NC}" exit 1 fi # Check if we should serve the documentation if [ "$1" = "--serve" ] || [ "$2" = "--serve" ] || [ "$3" = "--serve" ]; then echo -e "${BLUE}🌐 Starting development server...${NC}" echo "Documentation will be available at: http://localhost:3000" echo "Press Ctrl+C to stop the server" mdbook serve --open elif [ "$1" = "--watch" ] || [ "$2" = "--watch" ] || [ "$3" = "--watch" ]; then echo -e "${BLUE}👀 Starting file watcher...${NC}" echo "Documentation will be rebuilt automatically on file changes" echo "Press Ctrl+C to stop watching" mdbook watch else # Display build information echo "" echo -e "${GREEN}📚 Documentation built successfully!${NC}" echo "Output directory: $PROJECT_ROOT/book-output" echo "HTML files: $PROJECT_ROOT/book-output/html" echo "" echo "To serve the documentation locally:" echo " $0 --serve" echo "" echo "To watch for changes:" echo " $0 --watch" echo "" echo "To sync existing documentation:" echo " $0 --sync" echo "" echo "To build cargo documentation:" echo " $0 --cargo" echo "" echo "To build all documentation:" echo " $0 --all" fi # Generate documentation metrics echo -e "${BLUE}📊 Documentation metrics:${NC}" TOTAL_PAGES=$(find "$PROJECT_ROOT/book-output/html" -name "*.html" | wc -l) TOTAL_SIZE=$(du -sh "$PROJECT_ROOT/book-output/html" | cut -f1) echo " Total pages: $TOTAL_PAGES" echo " Total size: $TOTAL_SIZE" # Check for broken links if linkcheck is available if command -v mdbook-linkcheck &> /dev/null; then echo -e "${BLUE}🔗 Checking for broken links...${NC}" if mdbook-linkcheck; then echo -e "${GREEN}✅ No broken links found${NC}" else echo -e "${YELLOW}⚠️ Some links may be broken${NC}" fi fi # Build cargo documentation if requested if [ "$1" = "--cargo" ] || [ "$2" = "--cargo" ] || [ "$3" = "--cargo" ]; then echo -e "${BLUE}🦀 Building cargo documentation...${NC}" # Build cargo doc if cargo doc --no-deps --document-private-items; then echo -e "${GREEN}✅ Cargo documentation built successfully${NC}" # Enhance with logos if [ -f "$PROJECT_ROOT/scripts/docs/enhance-docs.sh" ]; then echo -e "${BLUE}🎨 Enhancing cargo docs with logos...${NC}" "$PROJECT_ROOT/scripts/docs/enhance-docs.sh" fi echo -e "${GREEN}✅ Cargo documentation enhanced with logos${NC}" else echo -e "${RED}❌ Cargo documentation build failed${NC}" fi fi # Build all documentation if requested if [ "$1" = "--all" ] || [ "$2" = "--all" ] || [ "$3" = "--all" ]; then echo -e "${BLUE}📚 Building all documentation...${NC}" # Build mdBook if mdbook build; then echo -e "${GREEN}✅ mdBook documentation built${NC}" else echo -e "${RED}❌ mdBook build failed${NC}" fi # Build cargo doc if cargo doc --no-deps --document-private-items; then echo -e "${GREEN}✅ Cargo documentation built${NC}" # Enhance with logos if [ -f "$PROJECT_ROOT/scripts/docs/enhance-docs.sh" ]; then echo -e "${BLUE}🎨 Enhancing cargo docs with logos...${NC}" "$PROJECT_ROOT/scripts/docs/enhance-docs.sh" fi else echo -e "${RED}❌ Cargo documentation build failed${NC}" fi fi echo "" echo -e "${GREEN}✨ Documentation build complete!${NC}"