gitpages.sh (4458B)
1 #!/bin/sh 2 # 3 # Copyright (c) 2026 Fred Großkopf 4 # 5 # Permission to use, copy, modify, and/or distribute this software for any 6 # purpose with or without fee is hereby granted, provided that the above 7 # copyright notice and this permission notice appear in all copies. 8 # 9 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 17 set -eu 18 19 DEFAULT_CONFIG="/etc/gitpages.conf" 20 21 # --- Secure Config Parser --- 22 get_config_val() { 23 awk -F '=' -v key="$1" ' 24 $1 ~ /^[[:space:]]*[a-zA-Z0-9_]+[[:space:]]*$/ && $1 == key { 25 gsub(/[[:space:]]+$/, "", $2); 26 gsub(/^[[:space:]]+/, "", $2); 27 print $2; 28 exit; 29 } 30 ' "$CONFIG" 31 } 32 33 # --- Utility Functions --- 34 error() { printf 'error: %s\n' "$1" >&2; exit 1; } 35 info() { printf 'info: %s\n' "$1"; } 36 sed_escape() { printf '%s\n' "$1" | sed 's/[\\/&]/\\&/g'; } 37 38 # --- Validation --- 39 check_deps() { 40 command -v stagit > /dev/null 2>&1 || error "stagit not found" 41 command -v stagit-index > /dev/null 2>&1 || error "stagit-index not found" 42 command -v git > /dev/null 2>&1 || error "git not found" 43 } 44 45 # --- Core Logic --- 46 generate_repo_html() { 47 rm -rf "$STAGING_DIR" 48 mkdir -p "$STAGING_DIR" 49 cd "$STAGING_DIR" 50 stagit "$REPO_SRC" >/dev/null 2>&1 51 } 52 53 process_repo_titles() { 54 [ -n "${REPO_TITLE_DESC:-}" ] || return 0 55 esc_desc=$(sed_escape "$REPO_TITLE_DESC") 56 find "$STAGING_DIR" -name "*.html" -type f -exec sed -i \ 57 "s| - $REPO_BASE - [^<]*| - $REPO_BASE$esc_desc|g" {} + 58 } 59 60 generate_index() { 61 # 1. Verify destination path exists 62 [ -d "$WEB_ROOT/$GIT_HTML" ] || { printf 'Error: %s/html not found\n' "$WEB_ROOT"; return 1; } 63 64 # 2. Use a subshell to avoid persistent cd and ensure we run from the right place 65 ( 66 cd "$WEB_ROOT/$GIT_HTML" || exit 1 67 68 # 3. Check if we have repositories before running the command 69 # Use 'find' to safely handle the list of repos 70 repos=$(find "$SRC_DIR" -maxdepth 1 -name "*.git") 71 72 if [ -n "$repos" ]; then 73 stagit-index $repos > index.html 74 else 75 printf 'No repositories found in %s\n' "$SRC_DIR" 76 fi 77 ) 78 } 79 80 process_index() { 81 [ -f "$WEB_ROOT/$GIT_HTML/index.html" ] || return 0 82 [ -n "${INDEX_TITLE:-}" ] && { 83 esc_title=$(sed_escape "$INDEX_TITLE") 84 sed -i "s|<title>Repositories</title>|<title>$esc_title</title>|g" "$WEB_ROOT/$GIT_HTML/index.html" 85 } 86 [ -n "${INDEX_DESC:-}" ] && { 87 esc_desc=$(sed_escape "$INDEX_DESC") 88 sed -i "s|<span class=\"desc\">Repositories</span>|<span class=\"desc\">$esc_desc</span>|g" "$WEB_ROOT/$GIT_HTML/index.html" 89 } 90 } 91 92 deploy_assets() { 93 [ -d "${ASSETS_DIR:-}" ] || return 0 94 mkdir -p "$STAGING_DIR" 95 for f in "$ASSETS_DIR"/*; do 96 [ -f "$f" ] && cp -L "$f" "$STAGING_DIR/$(basename "$f")" 97 done 98 } 99 100 swap_target_dir() { 101 rm -rf "$TARGET_DIR" 2>/dev/null || true 102 mv "$STAGING_DIR" "$TARGET_DIR" || error "could not move staging into place" 103 } 104 105 main() { 106 CONFIG="$DEFAULT_CONFIG" 107 while getopts ":c:h" opt; do 108 case "$opt" in 109 c) CONFIG="$OPTARG" ;; 110 h) printf 'usage: gitpages.sh [-c config] SRC\n'; exit 0 ;; 111 *) exit 1 ;; 112 esac 113 done 114 shift $((OPTIND - 1)) 115 [ $# -eq 1 ] || error "usage: gitpages.sh [-c config] SRC" 116 117 # Load configuration 118 [ -r "$CONFIG" ] || error "config $CONFIG not readable" 119 WEB_ROOT=$(get_config_val "WEB_ROOT") 120 GIT_HTML=$(get_config_val "GIT_HTML") 121 GIT_RAW=$(get_config_val "GIT_RAW") 122 INDEX_TITLE=$(get_config_val "INDEX_TITLE") 123 INDEX_DESC=$(get_config_val "INDEX_DESCRIPTION") 124 REPO_TITLE_DESC=$(get_config_val "REPO_TITLE_DESCRIPTION") 125 ASSETS_DIR=$(get_config_val "ASSETS_DIR") 126 127 # Setup environment context 128 check_deps 129 REPO_SRC="$1" 130 REPO_BASE=$(basename "$REPO_SRC" ".git") 131 SRC_DIR="$WEB_ROOT/$GIT_RAW" 132 VAR_DIR="${GITPAGES_VAR_DIR:-/var/gitpages}" 133 TARGET_DIR="$WEB_ROOT/$GIT_HTML/$REPO_BASE" 134 STAGING_DIR="$VAR_DIR/staging/$REPO_BASE" 135 136 # Execution 137 generate_repo_html 138 deploy_assets 139 process_repo_titles 140 swap_target_dir 141 generate_index 142 process_index 143 144 info "Generation complete for $REPO_BASE" 145 } 146 147 main "$@"