#!/usr/bin/env bash # setup.sh — Bootstrap a self-hosted Docsify site (static files; serve with # any web server — Apache, nginx, Caddy, …). # # Run this script from your web server's document root: # cd /path/to/docroot # bash setup.sh # # Downloads Docsify core, the search plugin, the Vue theme, the copy-to-clipboard # plugin, extra Prism languages (bash, yaml, python, r), KaTeX math, Mermaid diagrams, # and UX plugins (alerts, tabs, pagination, image zoom, collapsible sidebar) — # all served locally, no external CDN dependencies at runtime. Versions are pinned and # a SHA-256 manifest (assets/SHA256SUMS) is written for reproducible, verifiable rebuilds. # # Re-running in an existing site is safe: it refreshes the assets and regenerates # index.html, but never overwrites Markdown files (README.md, _sidebar.md, and any # pages you have added) that already exist. set -euo pipefail # ── Configuration ─────────────────────────────────────────────────────────────── # All versions are pinned exactly for reproducible, CDN-independent rebuilds. Bump a # version here, re-run, and commit the refreshed assets/ + assets/SHA256SUMS. DOCSIFY_VERSION="4.13.1" PRISM_VERSION="1.30.0" COPY_CODE_VERSION="3.0.2" FLEXIBLE_ALERTS_VERSION="1.3.0" TABS_VERSION="1.6.3" PAGINATION_VERSION="2.10.1" MEDIUM_ZOOM_VERSION="1.1.0" SIDEBAR_COLLAPSE_VERSION="1.3.5" MERMAID_VERSION="11.15.0" DOCSIFY_MERMAID_VERSION="2.0.1" # Math plugin and its KaTeX renderer are coupled: the standalone KaTeX stylesheet/font # version (KATEX_VERSION) MUST match the renderer bundled inside this docsify-katex # release. docsify-katex 1.4.4 bundles KaTeX 0.11.1 (the 2.x line is broken). Bump the # two together. DOCSIFY_KATEX_VERSION="1.4.4" KATEX_VERSION="0.11.1" # ── Argument check ────────────────────────────────────────────────────────────── if [[ $# -gt 0 ]]; then echo "This script takes no options (got: $*)." >&2 echo "Usage: bash setup.sh" >&2 exit 1 fi # ── Output helpers ────────────────────────────────────────────────────────────── GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[0;33m' RED='\033[0;31m' BOLD='\033[1m' NC='\033[0m' step() { echo -e "\n${BLUE}${BOLD}==> $1${NC}"; } ok() { echo -e " ${GREEN}✓${NC} $1"; } skip() { echo -e " ${YELLOW}•${NC} $1"; } die() { echo -e " ${RED}✗${NC} $1" >&2; exit 1; } # ── Preflight checks ──────────────────────────────────────────────────────────── step "Preflight checks" command -v curl &>/dev/null \ || die "curl is required but not found. Install curl and try again." ok "curl $(curl --version | head -1 | awk '{print $2}')" if [[ -f index.html ]]; then echo -e " ${RED}!${NC} index.html already exists in: $(pwd)" echo -e " This re-downloads assets and regenerates index.html (edits to" echo -e " index.html will be lost). Existing Markdown files are left untouched." read -r -p " Continue? [y/N] " reply || reply="" # EOF (non-interactive) -> treat as "no" [[ "${reply,,}" == "y" ]] || { echo "Aborted."; exit 0; } fi echo -e " Working in: $(pwd)" # ── Step 1: Directories ───────────────────────────────────────────────────────── step "Step 1 — Creating directory structure" mkdir -p assets/js assets/css assets/css/fonts ok "assets/js/" ok "assets/css/" # ── Step 2: Download Docsify assets ───────────────────────────────────────────── step "Step 2 — Downloading Docsify assets" CDN="https://cdn.jsdelivr.net/npm" fetch() { local url="$1" dest="$2" curl -sL --fail "$url" -o "$dest" || die "Failed to download: $url" ok "$dest" } # Core Docsify, search plugin, and theme fetch "${CDN}/docsify@${DOCSIFY_VERSION}/lib/docsify.min.js" assets/js/docsify.min.js fetch "${CDN}/docsify@${DOCSIFY_VERSION}/lib/plugins/search.min.js" assets/js/search.min.js fetch "${CDN}/docsify@${DOCSIFY_VERSION}/themes/vue.css" assets/css/vue.css # Copy-to-clipboard plugin (injects its own CSS — no separate stylesheet needed) fetch "${CDN}/docsify-copy-code@${COPY_CODE_VERSION}/dist/docsify-copy-code.min.js" assets/js/docsify-copy-code.min.js # Extra Prism languages. Docsify already bundles Prism core plus html/css/clike/ # javascript, so we only add the languages it does NOT ship. Never download # prism-core here — a standalone core replaces Docsify's Prism and silently breaks # highlighting for every language loaded after it. fetch "${CDN}/prismjs@${PRISM_VERSION}/components/prism-bash.min.js" assets/js/prism-bash.min.js fetch "${CDN}/prismjs@${PRISM_VERSION}/components/prism-yaml.min.js" assets/js/prism-yaml.min.js fetch "${CDN}/prismjs@${PRISM_VERSION}/components/prism-python.min.js" assets/js/prism-python.min.js fetch "${CDN}/prismjs@${PRISM_VERSION}/components/prism-r.min.js" assets/js/prism-r.min.js # Math: docsify-katex bundles the KaTeX renderer (version coupling is documented in the # Configuration block above). KaTeX's stylesheet loads its fonts from ./fonts/ beside it, # so the woff2 files go in assets/css/fonts/. Stylesheet + fonts are pinned to the bundled # KaTeX (KATEX_VERSION). fetch "${CDN}/docsify-katex@${DOCSIFY_KATEX_VERSION}/dist/docsify-katex.js" assets/js/docsify-katex.js fetch "${CDN}/katex@${KATEX_VERSION}/dist/katex.min.css" assets/css/katex.min.css # Read the woff2 font list out of the stylesheet, then download each. Guard against an # empty match: a future KaTeX bump could change the CSS format, which would otherwise # silently ship a site whose math renders without fonts. mapfile -t katex_fonts < <(grep -oE 'fonts/KaTeX_[^)]+\.woff2' assets/css/katex.min.css | sort -u) [[ ${#katex_fonts[@]} -gt 0 ]] \ || die "No KaTeX woff2 fonts found in katex.min.css; the CSS format may have changed for katex@${KATEX_VERSION}." for f in "${katex_fonts[@]}"; do curl -sL --fail "${CDN}/katex@${KATEX_VERSION}/dist/$f" -o "assets/css/$f" \ || die "Failed to download: $f" done ok "assets/css/fonts/ (${#katex_fonts[@]} KaTeX woff2 files)" # UX plugins — each loads after docsify; alerts/tabs/pagination inject their own CSS. fetch "${CDN}/docsify-plugin-flexible-alerts@${FLEXIBLE_ALERTS_VERSION}/dist/docsify-plugin-flexible-alerts.min.js" assets/js/docsify-plugin-flexible-alerts.min.js fetch "${CDN}/docsify-tabs@${TABS_VERSION}/dist/docsify-tabs.min.js" assets/js/docsify-tabs.min.js fetch "${CDN}/docsify-pagination@${PAGINATION_VERSION}/dist/docsify-pagination.min.js" assets/js/docsify-pagination.min.js # Image zoom: medium-zoom is a generic library (a hook in index.html applies it) + its CSS fetch "${CDN}/medium-zoom@${MEDIUM_ZOOM_VERSION}/dist/medium-zoom.min.js" assets/js/medium-zoom.min.js fetch "${CDN}/medium-zoom@${MEDIUM_ZOOM_VERSION}/dist/style.css" assets/css/medium-zoom.css # Collapsible sidebar — needs its own stylesheet fetch "${CDN}/docsify-sidebar-collapse@${SIDEBAR_COLLAPSE_VERSION}/dist/docsify-sidebar-collapse.min.js" assets/js/docsify-sidebar-collapse.min.js fetch "${CDN}/docsify-sidebar-collapse@${SIDEBAR_COLLAPSE_VERSION}/dist/sidebar.min.css" assets/css/sidebar.min.css # Diagrams: Mermaid (self-contained, ~3 MB) + docsify-mermaid, which turns ```mermaid # fenced blocks into diagrams (it uses Mermaid v11's run() API). fetch "${CDN}/mermaid@${MERMAID_VERSION}/dist/mermaid.min.js" assets/js/mermaid.min.js fetch "${CDN}/docsify-mermaid@${DOCSIFY_MERMAID_VERSION}/dist/docsify-mermaid.js" assets/js/docsify-mermaid.js # Record SHA-256 checksums of every downloaded asset. Commit assets/ together with this # manifest to rebuild without jsDelivr and to verify integrity later with: # (cd assets && sha256sum -c SHA256SUMS) ( cd assets && find . -type f ! -name SHA256SUMS \( -name '*.js' -o -name '*.css' -o -name '*.woff2' \) \ -print0 | sort -z | xargs -0 sha256sum > SHA256SUMS ) ok "assets/SHA256SUMS ($(wc -l < assets/SHA256SUMS) files)" # ── Step 3: index.html ────────────────────────────────────────────────────────── step "Step 3 — Creating index.html" # The heredoc delimiter is single-quoted ('HTML') so the shell does not expand # $docsify inside the template. cat > index.html << 'HTML' My Docs
HTML ok "index.html" # ── Step 4: Boilerplate content ───────────────────────────────────────────────── step "Step 4 — Boilerplate content (created only if missing)" # Write the heredoc on stdin to $1, but never clobber an existing file — so # re-running refreshes the assets and index.html without touching your content. write_boilerplate() { local dest="$1" local content content="$(cat)" if [[ -e "$dest" ]]; then skip "$dest (exists — left unchanged)" else printf '%s\n' "$content" > "$dest" ok "$dest" fi } write_boilerplate README.md << 'EOF' # Welcome This is the home page of your documentation site. Edit `README.md` to replace this content. ## Quick Links - [Getting Started](getting-started.md) EOF write_boilerplate _sidebar.md << 'EOF' - [Home](/) - [Getting Started](getting-started.md) EOF write_boilerplate getting-started.md << 'EOF' # Getting Started This is an example page. Replace this content with your own documentation. ## Section One Write your content here using standard Markdown. ## Section Two Docsify will automatically generate sidebar anchors for H2 headings when `subMaxLevel: 2` is set in `index.html`. EOF # ── Step 5: Permissions ───────────────────────────────────────────────────────── step "Step 5 — Setting file permissions" # Prune dotfiles/dotdirs so a docroot that is a Git repo keeps its .git/ perms intact. find . -path './.*' -prune -o -type f -exec chmod 644 {} \; find . -path './.*' -prune -o -type d -exec chmod 755 {} \; ok "Files: 644 | Directories: 755 (dotfiles left untouched)" # ── Verify ─────────────────────────────────────────────────────────────────────── step "Verifying file layout" find . -not -path '*/\.*' -not -path './assets/css/fonts*' | sort echo "./assets/css/fonts/ ($(ls -1 assets/css/fonts 2>/dev/null | wc -l) KaTeX woff2 files)" # ── Done ───────────────────────────────────────────────────────────────────────── echo "" echo -e "${GREEN}${BOLD}Setup complete.${NC}" echo -e "Open your site in a browser to verify." echo "" echo -e "Syntax highlighting: html, css, and javascript are built into Docsify;" echo -e "this setup adds bash, yaml, python, and r. To add more languages, download" echo -e "additional prism-.min.js files into assets/js/ and add their