diff options
Diffstat (limited to 'gen_web_hook.sh')
| -rw-r--r-- | gen_web_hook.sh | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/gen_web_hook.sh b/gen_web_hook.sh new file mode 100644 index 0000000..050b89d --- /dev/null +++ b/gen_web_hook.sh @@ -0,0 +1,342 @@ +#!/bin/bash +# gen_web_hook.sh — generates static HTML header/footer files for the +# Apache autoindex listing of the danix Slackware package repository. +# +# Can be called standalone or from slackrepo's HOOK_FINISH. +# Usage: gen_web_hook.sh [/path/to/pkgrepo] +# +# If no argument is given, uses $SR_PKGREPO if set, otherwise /repo. + +set -euo pipefail + +PKGREPO="${1:-${SR_PKGREPO:-/repo}}" + +log() { echo "gen_web_hook: $*"; } +warn() { echo "gen_web_hook: WARNING: $*" >&2; } + +if [ ! -d "$PKGREPO" ]; then + warn "PKGREPO '$PKGREPO' not found." + exit 1 +fi + +log "Generating static web files in $PKGREPO ..." + +# ── HTML escape ─────────────────────────────────────────────────────────────── +html_escape() { + local s="$1" + s="${s//&/&}" + s="${s//</<}" + s="${s//>/>}" + s="${s//\"/"}" + printf '%s' "$s" +} + +# ── Shared CSS ──────────────────────────────────────────────────────────────── +CSS='<style> +:root { + --bg: #0e1117; + --bg-card: #161b25; + --bg-hover: #1e2535; + --border: #2a3147; + --accent: #5c9cf5; + --accent-dim:#3a5f99; + --green: #4ec97b; + --accent2: #4ec97b; + --text: #c9d1e0; + --text-dim: #6b7a99; + --text-head: #e8edf7; + --mono: "IBM Plex Mono", monospace; + --sans: "IBM Plex Sans", sans-serif; +} +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } +body { + background: var(--bg); color: var(--text); font-family: var(--sans); + font-size: 15px; line-height: 1.6; min-height: 100vh; + display: flex; flex-direction: column; +} +.site-header { border-bottom: 1px solid var(--border); padding: 2rem 2.5rem 1.5rem; background: var(--bg-card); position: relative; overflow: hidden; } +.header-top { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; margin-bottom: 0.5rem; } +.site-title { font-family: var(--mono); font-size: 1.35rem; font-weight: 600; color: var(--text-head); } +.site-title span { color: var(--accent); } +.site-subtitle { font-size: 0.8rem; color: var(--text-dim); font-family: var(--mono); } +.header-desc { font-size: 0.9rem; color: var(--text-dim); max-width: 65ch; margin-bottom: 1rem; } +.header-links { display: flex; gap: 1.25rem; align-items: center; flex-wrap: wrap; } +.header-links a { + font-family: var(--mono); font-size: 0.8rem; color: var(--accent); text-decoration: none; + border: 1px solid var(--accent-dim); padding: 0.2rem 0.65rem; border-radius: 3px; +} +.header-links a:hover { background: var(--accent); color: var(--bg); } +pre { font-family: var(--mono) !important; color: var(--text) !important; background: transparent !important; padding: 1.5rem 2.5rem !important; flex: 1; } +table { width: calc(100% - 5rem); margin: 1.5rem 2.5rem; border-collapse: collapse; font-family: var(--mono); font-size: 0.85rem; } +th { text-align: left; color: var(--text-dim); font-weight: 600; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--border); } +td { padding: 0.4rem 0.75rem; border-bottom: 1px solid rgba(42,49,71,0.5); vertical-align: middle; } +tr:hover td { background: var(--bg-hover); } +td a, td a:visited { color: var(--text-head); text-decoration: none; } +td a[href$="/"]:not([href="../"]) { color: var(--accent); font-weight: 600; } +td a:hover { color: var(--accent); text-decoration: underline; } +td:nth-child(3), td:nth-child(4) { color: var(--text-dim); font-size: 0.8rem; } +td a[href$=".txz"] { color: var(--green); font-weight: 600; } +.autoindex-wrapper { flex: 1; display: flex; flex-direction: column; } +hr { display: none; } +address { display: none; } +.pkg-info { margin: 0 2.5rem 1.5rem; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; background: var(--bg-card); } +.pkg-info-header { display: flex; align-items: baseline; gap: 0.75rem; padding: 0.6rem 1rem; background: rgba(92,156,245,0.06); border-bottom: 1px solid var(--border); } +.pkg-info-label { font-family: var(--mono); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-dim); } +.pkg-info-name { font-family: var(--mono); font-size: 0.85rem; font-weight: 600; color: var(--accent); } +.pkg-info-body { padding: 0.75rem 1rem; font-size: 0.875rem; line-height: 1.65; color: var(--text); } +.pkg-info-body p { margin-bottom: 0.2rem; } +.pkg-title { font-weight: 600; color: var(--text-head); margin-bottom: 0.5rem !important; } +.pkg-meta { display: flex; gap: 1.5rem; flex-wrap: wrap; padding: 0.5rem 1rem; border-top: 1px solid var(--border); font-family: var(--mono); font-size: 0.75rem; } +.pkg-meta em { font-style: normal; margin-right: 0.3rem; color: var(--text-dim); } +.pkg-meta span { color: var(--text); } +.pkg-homepage { padding: 0.4rem 1rem 0.6rem; font-family: var(--mono); font-size: 0.78rem; border-top: 1px solid var(--border); } +.pkg-homepage a { color: var(--accent); text-decoration: none; } +.pkg-homepage a:hover { text-decoration: underline; } +.site-footer { border-top: 1px solid var(--border); background: var(--bg-card); padding: 1rem 2.5rem; margin-top: auto; } +.footer-inner { display: flex; flex-direction: column; gap: 0.35rem; } +.footer-meta { display: flex; align-items: center; gap: 1rem; margin-bottom: 0.2rem; } +.footer-updated { font-family: var(--mono); font-size: 0.8rem; color: var(--text-dim); } +.footer-date { color: var(--green); font-weight: 600; } +.footer-rss { display: inline-flex; align-items: center; gap: 0.3rem; font-family: var(--mono); font-size: 0.75rem; color: #e8923a; text-decoration: none; border: 1px solid rgba(232,146,58,0.3); padding: 0.15rem 0.5rem; border-radius: 3px; } +.footer-rss:hover { background: rgba(232,146,58,0.1); } +.footer-sig { font-family: var(--mono); font-size: 0.75rem; color: var(--text-dim); display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; } +.footer-sig a { color: var(--accent); text-decoration: none; } +.footer-sep { opacity: 0.3; } +.category-pill { position: absolute; top: 50%; right: 2.5rem; transform: translateY(-50%); z-index: 2; background: var(--bg-card); border: 1px solid var(--border); border-radius: 999px; padding: 0.4rem 1.25rem; font-family: var(--mono); font-size: 1.4rem; font-weight: 700; letter-spacing: 0.02em; text-transform: capitalize; } +.category-pill span { background: linear-gradient(135deg, var(--accent) 0%, var(--accent2) 100%); -webkit-background-clip: text; background-clip: text; color: transparent; } +</style>' + +# ── Shared footer signature ─────────────────────────────────────────────────── +footer_sig() { + cat << 'EOF' +<footer class="site-footer"> + <div class="footer-inner"> + <div class="footer-sig"> + <span>danix packages · Slackware64-current</span> + <span class="footer-sep">·</span> + <a href="https://danix.xyz/is/here/">Contact danix</a> + <span class="footer-sep">·</span> + <span>GPG signed · use at your own risk</span> + </div> + </div> +</footer> +</body> +</html> +EOF +} + +# ── Generate root _footer.html (last-updated + signature) ──────────────────── +generate_root_footer() { + local last_updated='unknown' + if [ -f "$PKGREPO/ChangeLog.txt" ]; then + last_updated=$(grep -m1 -E '^(Mon|Tue|Wed|Thu|Fri|Sat|Sun)' "$PKGREPO/ChangeLog.txt" || echo 'unknown') + fi + + { + cat << EOF +</div><!-- .autoindex-wrapper --> +<footer class="site-footer"> + <div class="footer-inner"> + <div class="footer-meta"> + <span class="footer-updated">Last updated: + <span class="footer-date">$(html_escape "$last_updated")</span> + </span> + <a class="footer-rss" href="/ChangeLog.rss"> + <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"> + <circle cx="5" cy="19" r="3"/> + <path d="M4 4a16 16 0 0 1 16 16h-3A13 13 0 0 0 4 7z"/> + <path d="M4 11a9 9 0 0 1 9 9H10a6 6 0 0 0-6-6z"/> + </svg> + RSS feed + </a> + </div> + <div class="footer-sig"> + <span>danix packages · Slackware64-current</span> + <span class="footer-sep">·</span> + <a href="https://danix.xyz/is/here/">Contact danix</a> + <span class="footer-sep">·</span> + <span>GPG signed · use at your own risk</span> + </div> + </div> +</footer> +</body> +</html> +EOF + } > "$PKGREPO/_footer.html" + log "Written: $PKGREPO/_footer.html" +} + +# ── Generate per-package _footer.html ──────────────────────────────────────── +generate_package_footer() { + local pkg_dir="$1" + local pkg_name + pkg_name=$(basename "$pkg_dir") + + local txt_file + txt_file=$(find "$pkg_dir" -maxdepth 1 -name '*.txt' | head -1) + + if [ -z "$txt_file" ]; then + # No .txt file — minimal footer + { echo '</div><!-- .autoindex-wrapper -->'; footer_sig; } > "$pkg_dir/_footer.html" + return + fi + + # Parse .txt + local title='' homepage='' body_lines=() + while IFS= read -r line; do + # Strip "pkgname:" or "pkgname: " prefix + local text + text=$(echo "$line" | sed 's/^[^:]*: \?//') + # Skip lines that were just "pkgname:" with nothing after + [[ "$line" == *: ]] && text='' + if [[ "$text" == Homepage:* ]]; then + homepage="${text#Homepage: }" + continue + fi + if [ -z "$title" ]; then + [ -n "$text" ] && title="$text" + else + [ -n "$text" ] && body_lines+=("$text") + fi + done < "$txt_file" + + # Parse .meta for sizes + local meta_file size_c='' size_u='' + meta_file=$(find "$pkg_dir" -maxdepth 1 -name '*.meta' | head -1) + if [ -n "$meta_file" ]; then + size_c=$(grep 'PACKAGE SIZE (compressed):' "$meta_file" | sed 's/.*: *//') + size_u=$(grep 'PACKAGE SIZE (uncompressed):' "$meta_file" | sed 's/.*: *//') + fi + + { + echo '</div><!-- .autoindex-wrapper -->' + echo '<section class="pkg-info">' + echo ' <div class="pkg-info-header">' + echo ' <span class="pkg-info-label">Package description</span>' + echo " <span class=\"pkg-info-name\">$(html_escape "$pkg_name")</span>" + echo ' </div>' + echo ' <div class="pkg-info-body">' + echo " <p class=\"pkg-title\">$(html_escape "$title")</p>" + for bline in "${body_lines[@]}"; do + echo " <p>$(html_escape "$bline")</p>" + done + echo ' </div>' + + if [ -n "$size_c" ] || [ -n "$size_u" ]; then + echo ' <div class="pkg-meta">' + [ -n "$size_c" ] && echo " <span><em>Size (compressed):</em> $(html_escape "$size_c")</span>" + [ -n "$size_u" ] && echo " <span><em>Size (uncompressed):</em> $(html_escape "$size_u")</span>" + echo ' </div>' + fi + + if [ -n "$homepage" ]; then + echo " <div class=\"pkg-homepage\"><a href=\"$(html_escape "$homepage")\" rel=\"noopener noreferrer\">$(html_escape "$homepage")</a></div>" + fi + + echo '</section>' + footer_sig + } > "$pkg_dir/_footer.html" + + log "Written: $pkg_dir/_footer.html" +} + +# ── Write _header.html to a given directory ─────────────────────────────────── +write_header() { + local dir="$1" + cat > "$dir/_header.html" << EOF +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>danix Slackware Repository</title> + <link rel="preconnect" href="https://fonts.googleapis.com"> + <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&family=IBM+Plex+Sans:wght@300;400;600&display=swap" rel="stylesheet"> + <script src="/.assets/matrix-rain.js" defer></script> + ${CSS} +</head> +<body> +<header class="site-header"> + <div class="header-top"> + <div class="site-title"><span>//</span> <a href="https://packages.danix.xyz" style="color:var(--accent)">danix packages</a></div> + <div class="site-subtitle">Slackware64-current · unofficial repository</div> + </div> + <p class="header-desc"> + Third-party Slackware packages built with + <a href="https://github.com/aclemons/slackrepo" style="color:var(--accent)">slackrepo</a> + on Slackware64-current. Use at your own risk. All packages are signed with my GPG key. + </p> + <div class="header-links"> + <a href="https://danix.xyz/is/here/">Contact me</a> + <a href="/ChangeLog.txt">ChangeLog</a> + <a href="/ChangeLog.rss">RSS</a> + </div> +</header> +<div class="autoindex-wrapper"> +EOF + log "Written: $dir/_header.html" +} + +# ── Write _header.html for a category directory (includes pill) ─────────────── +write_category_header() { + local dir="$1" + local cat_name + cat_name=$(basename "$dir") + cat > "$dir/_header.html" << EOF +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>danix Slackware Repository</title> + <link rel="preconnect" href="https://fonts.googleapis.com"> + <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&family=IBM+Plex+Sans:wght@300;400;600&display=swap" rel="stylesheet"> + <script src="/.assets/matrix-rain.js" defer></script> + ${CSS} +</head> +<body> +<header class="site-header"> + <div class="header-top"> + <div class="site-title"><span>//</span> <a href="https://packages.danix.xyz" style="color:var(--accent)">danix packages</a></div> + <div class="site-subtitle">Slackware64-current · unofficial repository</div> + </div> + <p class="header-desc"> + Third-party Slackware packages built with + <a href="https://github.com/aclemons/slackrepo" style="color:var(--accent)">slackrepo</a> + on Slackware64-current. Use at your own risk. All packages are signed with my GPG key. + </p> + <div class="header-links"> + <a href="https://danix.xyz/is/here/">Contact me</a> + <a href="/ChangeLog.txt">ChangeLog</a> + <a href="/ChangeLog.rss">RSS</a> + </div> + <div class="category-pill"><span>$(html_escape "$cat_name")</span></div> +</header> +<div class="autoindex-wrapper"> +EOF + log "Written: $dir/_header.html" +} + +# ── Generate category _footer.html (just the signature) ────────────────────── +generate_category_footer() { + local cat_dir="$1" + { echo '</div><!-- .autoindex-wrapper -->'; footer_sig; } > "$cat_dir/_footer.html" + log "Written: $cat_dir/_footer.html" +} + +# ── Main ────────────────────────────────────────────────────────────────────── +write_header "$PKGREPO" +generate_root_footer + +for category in "$PKGREPO"/*/; do + [ -d "$category" ] || continue + write_category_header "$category" + generate_category_footer "$category" + for pkg in "$category"*/; do + [ -d "$pkg" ] || continue + write_header "$pkg" + generate_package_footer "$pkg" + done +done + +log "Done." |
