#!/usr/bin/env bash # ============================================================================= # render_diagrams.sh — one-click render of all CS3319 architecture/flow diagrams. # # Renders every source under docs/diagrams/src/ into SVG (+ PDF where the tool # supports it) into docs/diagrams/svg/ and docs/diagrams/pdf/. # # .mmd -> Mermaid (mmdc, falls back to npx @mermaid-js/mermaid-cli) # .d2 -> D2 (d2, auto-installs via scoop on Windows if missing) # .dot -> Graphviz DOT (dot) # .puml -> PlantUML/C4 (java -jar scripts/.cache/plantuml.jar, auto-downloaded) # # The script degrades gracefully: a missing tool is reported and skipped, # it never aborts the whole run. Run it again after installing the missing tool. # # Usage: # bash scripts/render_diagrams.sh # render everything # bash scripts/render_diagrams.sh --svg # SVG only (skip PDFs) # ============================================================================= set -uo pipefail # ---- resolve paths relative to this script (works from any CWD) ---- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" SRC="$ROOT/docs/diagrams/src" SVG_DIR="$ROOT/docs/diagrams/svg" PDF_DIR="$ROOT/docs/diagrams/pdf" CACHE="$SCRIPT_DIR/.cache" PLANTUML_JAR="$CACHE/plantuml.jar" PLANTUML_URL="https://github.com/plantuml/plantuml/releases/latest/download/plantuml.jar" DO_PDF=1 [ "${1:-}" = "--svg" ] && DO_PDF=0 mkdir -p "$SVG_DIR" "$PDF_DIR" "$CACHE" # ---- color helpers (works in plain terminals too) ---- if [ -t 1 ]; then G=$'\033[32m'; Y=$'\033[33m'; R=$'\033[31m'; B=$'\033[1m'; N=$'\033[0m' else G=""; Y=""; R=""; B=""; N="" fi ok() { printf "%s✓%s %s\n" "$G" "$N" "$*"; } skip() { printf "%s·%s %s\n" "$Y" "$N" "$*"; } fail() { printf "%s✗%s %s\n" "$R" "$N" "$*"; } hdr() { printf "\n%s== %s ==%s\n" "$B" "$*" "$N"; } declare -a RESULTS=() record() { RESULTS+=("$1"); } # $1 = "✓|✗ file" # ---- tool availability (with best-effort auto-setup) ---- hdr "Checking renderers" # Mermaid (mmdc) MMDC="" if command -v mmdc >/dev/null 2>&1; then MMDC="mmdc"; elif command -v npx >/dev/null 2>&1; then MMDC="npx --yes @mermaid-js/mermaid-cli"; fi [ -n "$MMDC" ] && ok "Mermaid: $MMDC" || skip "Mermaid: NOT found (install: npm i -g @mermaid-js/mermaid-cli)" # D2 if ! command -v d2 >/dev/null 2>&1; then skip "d2 not found — attempting auto-install (scoop)..." if command -v scoop >/dev/null 2>&1; then scoop install d2 >/dev/null 2>&1 && ok "d2 installed via scoop" || skip "scoop install d2 failed" fi fi command -v d2 >/dev/null 2>&1 && ok "D2: $(command -v d2)" || skip "D2: NOT found (Windows: scoop install d2 | macOS: brew install d2)" # Graphviz command -v dot >/dev/null 2>&1 && ok "Graphviz: $(dot -V 2>&1)" || skip "Graphviz: NOT found (install graphviz)" # PlantUML (java + jar) PUML="" if command -v java >/dev/null 2>&1; then if [ ! -f "$PLANTUML_JAR" ]; then skip "plantuml.jar missing — downloading to $PLANTUML_JAR ..." if curl -L --fail -o "$PLANTUML_JAR" "$PLANTUML_URL" 2>/dev/null; then ok "plantuml.jar downloaded"; else rm -f "$PLANTUML_JAR"; skip "download failed (offline?)"; fi fi [ -f "$PLANTUML_JAR" ] && PUML="java -jar \"$PLANTUML_JAR\"" && ok "PlantUML: $(java -jar "$PLANTUML_JAR" -version 2>/dev/null | head -1)" else skip "PlantUML: java NOT found (install a JDK)" fi # ---- helpers ---- have() { command -v "$1" >/dev/null 2>&1; } # ===================================================================== # Mermaid (.mmd) # ===================================================================== hdr "Mermaid (.mmd)" if [ -n "$MMDC" ]; then shopt -s nullglob for f in "$SRC"/*.mmd; do b="$(basename "$f" .mmd)" if $MMDC -i "$f" -o "$SVG_DIR/$b.svg" >/dev/null 2>&1; then ok "$b.svg"; record "ok $b.mmd"; else fail "$b (mmdc error)"; record "bad $b.mmd"; fi if [ "$DO_PDF" = 1 ] && $MMDC -i "$f" -o "$PDF_DIR/$b.pdf" >/dev/null 2>&1; then :; fi done shopt -u nullglob else skip "no .mmd rendered (mmdc missing)" fi # ===================================================================== # D2 (.d2) # ===================================================================== hdr "D2 (.d2)" if have d2; then shopt -s nullglob for f in "$SRC"/*.d2; do b="$(basename "$f" .d2)" if d2 "$f" "$SVG_DIR/$b.svg" >/dev/null 2>&1; then ok "$b.svg"; record "ok $b.d2"; else fail "$b (d2 error)"; record "bad $b.d2"; fi [ "$DO_PDF" = 1 ] && d2 "$f" "$PDF_DIR/$b.pdf" >/dev/null 2>&1 || true done shopt -u nullglob else skip "no .d2 rendered (d2 missing)" fi # ===================================================================== # Graphviz (.dot) # ===================================================================== hdr "Graphviz (.dot)" if have dot; then shopt -s nullglob for f in "$SRC"/*.dot; do b="$(basename "$f" .dot)" if dot -Tsvg "$f" -o "$SVG_DIR/$b.svg" >/dev/null 2>&1; then ok "$b.svg"; record "ok $b.dot"; else fail "$b (dot error)"; record "bad $b.dot"; fi [ "$DO_PDF" = 1 ] && dot -Tpdf "$f" -o "$PDF_DIR/$b.pdf" >/dev/null 2>&1 || true done shopt -u nullglob else skip "no .dot rendered (graphviz missing)" fi # ===================================================================== # PlantUML / C4 (.puml) # ===================================================================== hdr "PlantUML / C4 (.puml)" if [ -n "$PUML" ]; then shopt -s nullglob for f in "$SRC"/*.puml; do b="$(basename "$f" .puml)" # plantuml writes .svg next to the source by default; render then move. if ( cd "$SRC" && eval "$PUML" -tsvg -charset UTF-8 "$(basename "$f")" >/dev/null 2>&1 ); then if [ -f "$SRC/$b.svg" ]; then mv -f "$SRC/$b.svg" "$SVG_DIR/$b.svg"; ok "$b.svg"; record "ok $b.puml"; else fail "$b (no svg produced)"; record "bad $b.puml"; fi else fail "$b (plantuml error)"; record "bad $b.puml" fi if [ "$DO_PDF" = 1 ]; then ( cd "$SRC" && eval "$PUML" -tpdf -charset UTF-8 "$(basename "$f")" >/dev/null 2>&1 ) && \ [ -f "$SRC/$b.pdf" ] && mv -f "$SRC/$b.pdf" "$PDF_DIR/$b.pdf" || true fi done shopt -u nullglob else skip "no .puml rendered (java/plantuml.jar missing)" fi # ===================================================================== # Summary # ===================================================================== hdr "Summary" SVG_COUNT=$(find "$SVG_DIR" -maxdepth 1 -name '*.svg' 2>/dev/null | wc -l | tr -d ' ') PDF_COUNT=$(find "$PDF_DIR" -maxdepth 1 -name '*.pdf' 2>/dev/null | wc -l | tr -d ' ') BAD=$(printf '%s\n' "${RESULTS[@]}" 2>/dev/null | grep -c '^bad' || true) printf "SVG: %s PDF: %s failed sources: %s\n" "$SVG_COUNT" "$PDF_COUNT" "$BAD" [ "$BAD" = "0" ] && ok "all available sources rendered" || fail "$BAD source(s) failed — see messages above" printf "SVG dir: %s\nPDF dir: %s\n" "$SVG_DIR" "$PDF_DIR" exit 0