208 lines
8.7 KiB
Python
208 lines
8.7 KiB
Python
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import yaml
|
|
import re
|
|
|
|
# Configuration
|
|
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
BUILD_DIR = os.path.join(ROOT_DIR, "_site_src")
|
|
DOCS_DIR = os.path.join(BUILD_DIR, "docs")
|
|
MKDOCS_CONFIG = os.path.join(ROOT_DIR, "mkdocs.yml")
|
|
GEMINI_CLI = os.path.join(ROOT_DIR, ".gemini", "gemini_cli.py")
|
|
|
|
def clean_build_dir():
|
|
if os.path.exists(BUILD_DIR):
|
|
try:
|
|
shutil.rmtree(BUILD_DIR)
|
|
except Exception as e:
|
|
print(f"Warning cleaning build dir: {e}")
|
|
|
|
os.makedirs(DOCS_DIR, exist_ok=True)
|
|
|
|
def clean_folder_name(name):
|
|
# Converts 'documentacao zammad' to 'Zammad'
|
|
# Converts 'documentacao backup-restore' to 'Backup Restore'
|
|
if name.startswith("documentacao "):
|
|
name = name.replace("documentacao ", "")
|
|
|
|
# Capitalize words
|
|
return name.title()
|
|
|
|
def process_markdown_content(content):
|
|
# Remove the Manual Revision History Block
|
|
# Look for ## 1. HISTÓRICO DE REVISÃO until the next ## header
|
|
# Pattern: ## 1. HISTÓRICO... (anything until next ## or end)
|
|
pattern = r"## 1\. HISTÓRICO DE REVISÃO.*?(?=## |\Z)"
|
|
content = re.sub(pattern, "", content, flags=re.DOTALL)
|
|
|
|
# Also strip any numbering from H2 headers if needed, but user might want them.
|
|
return content
|
|
|
|
def copy_manuals():
|
|
print("Copying manuals to build directory...")
|
|
# Find all 'documentacao *' folders
|
|
for item in os.listdir(ROOT_DIR):
|
|
if os.path.isdir(os.path.join(ROOT_DIR, item)) and item.startswith("documentacao"):
|
|
src_path = os.path.join(ROOT_DIR, item)
|
|
|
|
# Nice name for the folder
|
|
clean_name = clean_folder_name(item)
|
|
dst_path = os.path.join(DOCS_DIR, clean_name)
|
|
|
|
print(f"Processing {item} -> {clean_name}")
|
|
shutil.copytree(src_path, dst_path, dirs_exist_ok=True)
|
|
|
|
# Post-process files in the new destination
|
|
for root, dirs, files in os.walk(dst_path):
|
|
for file in files:
|
|
if file.lower().endswith('.md'):
|
|
# Inject PDF Link + Strip Revision History
|
|
full_path = os.path.join(root, file)
|
|
|
|
# Generate/Check PDF name
|
|
pdf_name = file.rsplit('.', 1)[0] + ".pdf"
|
|
|
|
try:
|
|
with open(full_path, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
|
|
modified_content = process_markdown_content(content)
|
|
|
|
# Add PDF Button
|
|
display_name = "Baixar PDF"
|
|
link = f'<a class="md-button md-button--primary download-pdf-btn" href="./{pdf_name}">:material-file-pdf-box: {display_name}</a>\n\n'
|
|
|
|
# Insert link after header or at top
|
|
if modified_content.startswith("---"):
|
|
# Split frontmatter
|
|
parts = modified_content.split("---", 2)
|
|
if len(parts) >= 3:
|
|
parts[2] = link + parts[2]
|
|
modified_content = "---".join(parts)
|
|
else:
|
|
modified_content = link + modified_content
|
|
else:
|
|
# Try to find H1
|
|
if "# " in modified_content:
|
|
modified_content = modified_content.replace("\n# ", f"\n{link}\n# ", 1) # Put before? No, after is better.
|
|
# Actually, let's put it AT THE TOP of content, below title if possible.
|
|
# Simple regex replace for first H1
|
|
modified_content = re.sub(r'(^# .+)', r'\1\n\n' + link, modified_content, count=1, flags=re.MULTILINE)
|
|
else:
|
|
modified_content = link + modified_content
|
|
|
|
with open(full_path, 'w', encoding='utf-8') as f:
|
|
f.write(modified_content)
|
|
except Exception as e:
|
|
print(f"Error processing {file}: {e}")
|
|
|
|
|
|
# Copy contents of 'assets' folder in root to 'docs/assets'
|
|
root_assets = os.path.join(ROOT_DIR, "assets")
|
|
docs_assets = os.path.join(DOCS_DIR, "assets")
|
|
if os.path.exists(root_assets):
|
|
print(f"Copying root assets from {root_assets} to {docs_assets}")
|
|
shutil.copytree(root_assets, docs_assets, dirs_exist_ok=True)
|
|
|
|
def create_index():
|
|
print("Creating index.md from README.md...")
|
|
readme_path = os.path.join(ROOT_DIR, "README.md")
|
|
if not os.path.exists(readme_path):
|
|
print("README.md not found, creating basic index.")
|
|
# Basic index fallback
|
|
dirs = [d for d in os.listdir(DOCS_DIR) if os.path.isdir(os.path.join(DOCS_DIR, d)) and d != "assets"]
|
|
dirs.sort()
|
|
content = "# Base de Conhecimento - iT Guys\n\n## Manuais Disponíveis\n\n"
|
|
content += "\n".join([f"- [{d}](./{d}/)" for d in dirs])
|
|
with open(os.path.join(DOCS_DIR, "index.md"), 'w', encoding='utf-8') as f:
|
|
f.write(content)
|
|
return
|
|
|
|
with open(readme_path, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
|
|
# Build a map of all .md files in DOCS_DIR
|
|
# Format: { "filename_no_ext": "relative/path/to/file.md" }
|
|
file_map = {}
|
|
for root, dirs, files in os.walk(DOCS_DIR):
|
|
for file in files:
|
|
if file.lower().endswith('.md') and file.lower() != 'index.md':
|
|
filename_no_ext = os.path.splitext(file)[0]
|
|
rel_path = os.path.relpath(os.path.join(root, file), DOCS_DIR)
|
|
file_map[filename_no_ext.strip().lower()] = rel_path.replace("\\", "/")
|
|
|
|
# Regex to find checklist items: - [x] [Nível X] Title
|
|
# We want to capture the status [x] or [ ] and the title [Nível X] Title
|
|
def replace_with_link(match):
|
|
status = match.group(1)
|
|
title = match.group(2).strip()
|
|
|
|
# Try to find the title in our file map
|
|
# We need to be careful with exact matches
|
|
key = title.lower()
|
|
if key in file_map:
|
|
return f"- [{status}] [{title}]({file_map[key]})"
|
|
else:
|
|
# Try matching without Nivel prefix if it fails?
|
|
# No, keep it strict or try cleaning.
|
|
# Example: [Nível 2] Interfaces, Vlans E Lagg
|
|
return f"- [{status}] {title} (Arquivo não encontrado)"
|
|
|
|
# Regex pattern: - \[(x| )\] (.*)
|
|
# Match lines starting with - [x] or - [ ]
|
|
pattern = r"^- \[(x| )\] (.*)"
|
|
new_content = re.sub(pattern, replace_with_link, content, flags=re.MULTILINE)
|
|
|
|
with open(os.path.join(DOCS_DIR, "index.md"), 'w', encoding='utf-8') as f:
|
|
f.write(new_content)
|
|
|
|
def generate_pdfs():
|
|
print("Generating PDFs...")
|
|
# Call gemini_cli batch_convert on the DOCS_DIR
|
|
# We need to make sure we use the same python env
|
|
subprocess.check_call([sys.executable, GEMINI_CLI, "batch-convert", DOCS_DIR])
|
|
|
|
def build_mkdocs():
|
|
print("Building MkDocs site...")
|
|
|
|
# Load base config
|
|
# Use unsafe_load to handle !!python tags
|
|
with open(os.path.join(ROOT_DIR, "mkdocs.yml"), "r") as f:
|
|
base_config = yaml.unsafe_load(f)
|
|
|
|
# Update config to point to our source folder
|
|
# IMPORTANT: We use relative path so MkDocs can find it from ROOT
|
|
base_config["docs_dir"] = "_site_src/docs"
|
|
base_config["site_dir"] = "_site_src/site"
|
|
|
|
# Ensure extra_css is copied
|
|
extra_css_src = os.path.join(ROOT_DIR, ".gemini", "stylesheets")
|
|
extra_css_dst = os.path.join(DOCS_DIR, "stylesheets")
|
|
if os.path.exists(extra_css_src):
|
|
shutil.copytree(extra_css_src, extra_css_dst, dirs_exist_ok=True)
|
|
|
|
# Write temporary config in ROOT (so it sees .git)
|
|
temp_config_path = os.path.join(ROOT_DIR, "mkdocs_generated.yml")
|
|
with open(temp_config_path, "w") as f:
|
|
yaml.dump(base_config, f)
|
|
|
|
# Run build using the generated config in ROOT
|
|
subprocess.check_call(["mkdocs", "build", "-f", "mkdocs_generated.yml"])
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
clean_build_dir()
|
|
copy_manuals()
|
|
create_index()
|
|
generate_pdfs()
|
|
build_mkdocs()
|
|
print("Build Complete! Site is in _site_src/site")
|
|
except Exception as e:
|
|
print(f"Build Failed: {e}")
|
|
# print stack trace
|
|
import traceback
|
|
traceback.print_exc()
|
|
sys.exit(1)
|