#!/bin/sh # docs-compile.sh: Markdown-to-HTML compilation pipeline # # Converts markdown source in docs/public/ to HTML, applying .docignore filtering # and version-dated output directories. # # Usage: # docs-compile.sh [OPTIONS] # # Options: # --semver VERSION Semantic version (e.g., 0.8.2). Defaults to git tag or 0.0.0 # --date DATE Build date DD.mon.YYYY (e.g., 24.mar.2026). Defaults to today # --filter FILE Path to .docignore filter file. Defaults to docs/public/.docignore # --source DIR Source markdown directory. Defaults to docs/public/ # --language LANG Compile single language (e.g., sl, en, de). If omitted, compiles docs/public/ root # --help Print usage # # Example: # docs-compile.sh --semver 0.8.2 /usr/local/www/docs.clawdie.si/ # # Outputs to: /usr/local/www/docs.clawdie.si/docs-v0.8.2_24.mar.2026/ # # docs-compile.sh --semver 0.8.2 --language sl /usr/local/www/docs.clawdie.si/ # # Outputs to: /usr/local/www/docs.clawdie.si/docs-v0.8.2_24.mar.2026/sl/ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" DATE_FORMAT_SCRIPT="${REPO_DIR}/scripts/date-format.sh" # Defaults SEMVER="" BUILD_DATE=$(bash "$DATE_FORMAT_SCRIPT" display-date | tr 'A-Z' 'a-z') FILTER_FILE="docs/public/.docignore" SOURCE_DIR="docs/public" OUTPUT_BASE="" LANGUAGE="" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log_msg() { echo "$(bash "$DATE_FORMAT_SCRIPT" display-ts) [compile] $1"; } log_err() { echo "$(bash "$DATE_FORMAT_SCRIPT" display-ts) ${RED}[ERROR]${NC} $1" >&2; } # Parse arguments while [ $# -gt 0 ]; do case "$1" in --semver) SEMVER="$2"; shift 2 ;; --date) BUILD_DATE="$2"; shift 2 ;; --filter) FILTER_FILE="$2"; shift 2 ;; --source) SOURCE_DIR="$2"; shift 2 ;; --language) LANGUAGE="$2"; shift 2 ;; --help) cat < Options: --semver VERSION Semantic version (e.g., 0.8.2). Auto-detect from git tag if omitted --date DATE Build date DD.mon.YYYY (e.g., 24.mar.2026). Defaults to today --filter FILE Path to .docignore filter. Defaults to docs/public/.docignore --source DIR Source markdown directory. Defaults to docs/public/ --language LANG Compile single language (e.g., sl, en). Omit for root docs/public/ --help Print this message Example: $(basename "$0") --semver 0.8.2 /usr/local/www/docs.clawdie.si/ # Output: /usr/local/www/docs.clawdie.si/docs-v0.8.2_20260324/ EOF exit 0 ;; -*) log_err "Unknown option: $1"; exit 1 ;; *) OUTPUT_BASE="$1"; shift ;; esac done # Validate required arguments [ -z "$OUTPUT_BASE" ] && { log_err "output-dir required"; exit 1; } # Adjust SOURCE_DIR for language-specific compilation if [ -n "$LANGUAGE" ]; then SOURCE_DIR="${SOURCE_DIR}/${LANGUAGE}" fi [ -d "$SOURCE_DIR" ] || { log_err "Source directory not found: $SOURCE_DIR"; exit 1; } # Auto-detect SEMVER from git tag if not provided if [ -z "$SEMVER" ]; then SEMVER=$(git describe --tags --match 'v*' 2>/dev/null | sed 's/^v//' | cut -d'-' -f1) [ -z "$SEMVER" ] && SEMVER="0.0.0" log_msg "Auto-detected semver: $SEMVER" fi # Validate date format (DD.mon.YYYY, e.g., 24.mar.2026) if ! echo "$BUILD_DATE" | grep -qE '^[0-3][0-9]\.(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\.[0-9]{4}$'; then log_err "Invalid BUILD_DATE: $BUILD_DATE (expected DD.mon.YYYY, e.g., 24.mar.2026)"; exit 1 fi # Create versioned output directory (with language subdirectory if specified) VERSION_DIR="${OUTPUT_BASE}/docs-v${SEMVER}_${BUILD_DATE}" if [ -n "$LANGUAGE" ]; then VERSION_DIR="${VERSION_DIR}/${LANGUAGE}" fi mkdir -p "$VERSION_DIR" log_msg "Output: $VERSION_DIR" # No external dependencies required — using simple HTML wrapper # ──────────────────────────────────────────────────────────────────────────── # Build filter: Convert .docignore patterns (glob) to rsync exclude list # ──────────────────────────────────────────────────────────────────────────── EXCLUDE_ARGS="" if [ -f "$FILTER_FILE" ]; then while IFS= read -r pattern; do # Skip empty lines and comments [ -z "$pattern" ] && continue echo "$pattern" | grep -q '^[[:space:]]*#' && continue # Convert glob to rsync exclude pattern EXCLUDE_ARGS="$EXCLUDE_ARGS --exclude=$pattern" done < "$FILTER_FILE" log_msg "Loaded filter: $FILTER_FILE" else log_msg "Warning: Filter file not found: $FILTER_FILE (compiling all files)" fi # ──────────────────────────────────────────────────────────────────────────── # Copy markdown files, applying exclusions # ──────────────────────────────────────────────────────────────────────────── # Temporarily stage filtered files to a work directory for processing mkdir -p "${REPO_DIR}/tmp" WORK_DIR=$(mktemp -d "${REPO_DIR}/tmp/docs-compile.XXXXXXXX") trap "rm -rf $WORK_DIR" EXIT # Use rsync to copy with exclusions (safer than cp with complicated patterns) rsync -av \ --include='*.md' \ --exclude='*.md' \ --exclude='*' \ $EXCLUDE_ARGS \ "$SOURCE_DIR/" "$WORK_DIR/" 2>&1 | grep -E "^\.md|/$" || true log_msg "Filtered markdown copied to work directory" # ──────────────────────────────────────────────────────────────────────────── # Compile markdown → HTML # ──────────────────────────────────────────────────────────────────────────── # Helper: Wrap markdown in HTML (simple, no external dependencies) wrap_markdown_html() { local md_file="$1" local html_file="$2" local title=$(basename "$md_file" .md) # Extract first heading for title title=$(grep -m1 '^#' "$md_file" | sed 's/^#+[[:space:]]*//' || echo "$title") # Generate table of contents from markdown headings local toc="" toc=$(grep '^##[^#]' "$md_file" | sed 's/^## *//' | while read line; do echo "
  • $line
  • " done) cat > "$html_file" << HTMLEOF $title — Clawdie-AI Docs

    $title

    Contents:
      $toc
    $(cat "$md_file" | sed -e 's/^#[^#]//' -e 's/^## \(.*\)/

    \1<\/h2>/' -e 's/^### \(.*\)/

    \1<\/h3>/' | sed -e 's/\*\*\([^*]*\)\*\*/\1<\/strong>/g' -e 's/\*\([^*]*\)\*/\1<\/em>/g' -e 's/^\- /
  • /g' | awk ' BEGIN { in_code = 0; in_list = 0 } /^```/ { in_code = !in_code; if (in_code) print "
    "; else print "
    "; next } in_code { print; next } /^
  • / { if (!in_list) { print "
      "; in_list = 1 } gsub(/
    • /, "
    • "); print $0; next } /^$/ { if (in_list) { print "
    "; in_list = 0 }; next } /^> / { gsub(/^> /, ""); print "
    " $0 "
    "; next } { if (NF > 0) print "

    " $0 "

    " } END { if (in_list) print "" } ')
  • HTMLEOF } FILE_COUNT=0 for md_file in $(find "$WORK_DIR" -name "*.md" -type f); do # Compute relative path rel_path="${md_file#${WORK_DIR}/}" # Output HTML path (same structure, .html extension) html_file="${VERSION_DIR}/${rel_path%.md}.html" html_dir=$(dirname "$html_file") mkdir -p "$html_dir" # Wrap markdown in HTML if wrap_markdown_html "$md_file" "$html_file" 2>&1; then FILE_COUNT=$((FILE_COUNT + 1)) log_msg "✓ $rel_path → $(basename "$html_file")" else log_err "Failed to compile: $rel_path" exit 1 fi done [ $FILE_COUNT -eq 0 ] && { log_err "No markdown files found after filtering"; exit 1; } log_msg "Compiled $FILE_COUNT files" # ──────────────────────────────────────────────────────────────────────────── # Create index.html (table of contents for directory listing) # ──────────────────────────────────────────────────────────────────────────── INDEX_FILE="${VERSION_DIR}/index.html" cat > "$INDEX_FILE" << 'INDEXEOF' Clawdie-AI Documentation Index

    📚 Clawdie-AI Documentation

    Generated: GENERATED_AT | Version: VERSION_STR

      INDEXEOF # Find all generated HTML files and create list find "$VERSION_DIR" -name "*.html" ! -name "index.html" -type f | sort | while read html; do rel_path="${html#${VERSION_DIR}/}" # Convert to display name (remove .html, replace dashes with spaces) display_name=$(basename "$rel_path" .html | sed 's/-/ /g') echo "
    • $display_name
    • " >> "$INDEX_FILE" done cat >> "$INDEX_FILE" << 'INDEXEOF'

    Note: Internal documentation (marked with -INTERNAL, -SENSITIVE, or in private/ directories) is excluded from public deployment.

    INDEXEOF # Replace placeholders (use | as separator to avoid issues with / and : in dates) GENERATED_AT=$(date +'%Y-%m-%d %H:%M:%S') sed -i '' "s|GENERATED_AT|$GENERATED_AT|g" "$INDEX_FILE" sed -i '' "s|VERSION_STR|v${SEMVER} (${BUILD_DATE})|g" "$INDEX_FILE" log_msg "✓ Created index.html" # ──────────────────────────────────────────────────────────────────────────── # Validation # ──────────────────────────────────────────────────────────────────────────── # Verify directory structure if [ ! -f "${VERSION_DIR}/index.html" ]; then log_err "index.html not created"; exit 1 fi HTML_COUNT=$(find "$VERSION_DIR" -name "*.html" | wc -l) log_msg "Validation: $HTML_COUNT HTML files" # ──────────────────────────────────────────────────────────────────────────── # Output summary # ──────────────────────────────────────────────────────────────────────────── echo "" echo -e "${GREEN}✓ Compilation complete${NC}" echo " Version: v${SEMVER}" echo " Date: ${BUILD_DATE}" echo " Output: $VERSION_DIR" echo " Files: $HTML_COUNT" echo "" echo "Next step: Deploy with symlink swap" echo " ln -sfn docs-v${SEMVER}_${BUILD_DATE} ${OUTPUT_BASE}/docs-current" echo "" exit 0