gitpages

A collection of scripts to securely webhost and publish git repositories.
git clone https://scm.kuandu.systems/git-raw/gitpages.git
Log | Files | Refs | README | LICENSE

commit 12363605cdb955059c3d41eea7ca058a161407f1
parent 1ae7753fc298125003c2182a895342907d71a7b0
Author: Fred Großkopf <fred@kuandu.systems>
Date:   Tue, 28 Apr 2026 22:15:07 +0200

WIP: updates gitpages.sh

Diffstat:
Mgitpages.sh | 277++++++++++++++++++++++++-------------------------------------------------------
Mtest-gitpages.sh | 417++++++++++---------------------------------------------------------------------
2 files changed, 132 insertions(+), 562 deletions(-)

diff --git a/gitpages.sh b/gitpages.sh @@ -1,240 +1,129 @@ #!/bin/sh +# gitpages.sh - Regenerate static git pages +# Usage: gitpages.sh [-c config] SRC set -eu -# === CONSTANTS === - -CONFIG_FILE_NAME="gitpages.conf" - -usage() { - cat << EOF -usage: $(basename "$0") [-c CONFIG_FILE] SRC DST_DIR [ASSETS_DIR] - -Generates static git HTML pages + index using stagit. - -Arguments: - SRC Single git repo OR directory containing *.git repos - DST_DIR Output directory for HTML pages - ASSETS_DIR Optional: CSS, favicon, etc. directory - -Options: - -c CONFIG_FILE Path to config (default: ./$CONFIG_FILE_NAME) - -h Show this help - -Examples: - $(basename "$0") /path/to/repo.git /var/www/git - $(basename "$0") /path/to/repos /var/www/git - $(basename "$0") -c /etc/$CONFIG_FILE_NAME /path/to/repos /var/www/git -EOF +# Default configuration path +DEFAULT_CONFIG="/etc/gitpages.conf" + +# --- Secure Config Parser --- +get_config_val() { + awk -F '=' -v key="$1" ' + $1 ~ /^[[:space:]]*[a-zA-Z0-9_]+[[:space:]]*$/ && $1 == key { + gsub(/[[:space:]]+$/, "", $2); + gsub(/^[[:space:]]+/, "", $2); + print $2; + exit; + } + ' "$CONFIG" } -# === UTILITY FUNCTIONS === -error() { - printf 'error: %s\n' "$1" >&2 - exit 1 -} -warning() { printf 'warning: %s\n' "$1" >&2; } +# --- Utility Functions --- +error() { printf 'error: %s\n' "$1" >&2; exit 1; } info() { printf 'info: %s\n' "$1"; } - -repo_base() { basename "$1" ".git"; } -html_dir_for() { printf '%s/%s' "$2" "$(repo_base "$1")"; } - sed_escape() { printf '%s\n' "$1" | sed 's/[\\/&]/\\&/g'; } -# === VALIDATION === -check_args() { - case $# in 2 | 3) ;; *) error "usage: $0 [-c CONFIG_FILE] SRC DST_DIR [ASSETS_DIR]" ;; esac -} - -check_html_writable() { - [ -d "$DST_DIR" ] || error "$DST_DIR does not exist" - [ -w "$DST_DIR" ] || error "cannot write to $DST_DIR" -} - -check_stagit() { +# --- Validation --- +check_deps() { command -v stagit > /dev/null 2>&1 || error "stagit not found" command -v stagit-index > /dev/null 2>&1 || error "stagit-index not found" + command -v git > /dev/null 2>&1 || error "git not found" } -check_assets() { - [ -n "${ASSETS_DIR:-}" ] || return 0 - [ -d "$ASSETS_DIR" ] || error "ASSETS_DIR '$ASSETS_DIR' is not a directory" - [ -r "$ASSETS_DIR" ] || error "cannot read ASSETS_DIR '$ASSETS_DIR'" - - found=0 - for f in "$ASSETS_DIR"/*; do [ -e "$f" ] && { - found=1 - break - }; done - [ "$found" -eq 1 ] || warning "ASSETS_DIR '$ASSETS_DIR' is empty" -} - -is_git_repo() { git -C "$1" rev-parse --git-dir > /dev/null 2>&1; } -has_git_repos() { - for repo in "$1"/*.git; do [ -e "$repo" ] && return 0; done - false -} - -# === CORE FUNCTIONS === +# --- Core Logic --- generate_repo_html() { - repo="$1" dst_dir="$2" repo_name="$3" - html_repo_dir="$dst_dir/$repo_name" - - rm -rf "$html_repo_dir" - mkdir -p "$html_repo_dir" - cd "$html_repo_dir" - stagit "$repo" > /dev/null 2>&1 + rm -rf "$TARGET_DIR" + mkdir -p "$TARGET_DIR" + cd "$TARGET_DIR" + stagit "$REPO_SRC" >/dev/null 2>&1 } process_repo_titles() { - repo="$1" repo_name="$2" html_repo_dir="$3" - [ -d "$html_repo_dir" ] || return 0 [ -n "${REPO_TITLE_DESC:-}" ] || return 0 - - esc_description=$(sed_escape "$REPO_TITLE_DESC") - find "$html_repo_dir" -name "*.html" -type f -exec sed -i \ - "s| - $repo_name - [^<]*| - $repo_name$esc_description|g" {} + + esc_desc=$(sed_escape "$REPO_TITLE_DESC") + find "$TARGET_DIR" -name "*.html" -type f -exec sed -i \ + "s| - $REPO_BASE - [^<]*| - $REPO_BASE$esc_desc|g" {} + } generate_index() { - cd "$DST_DIR" - stagit-index "$SRC_DIR"/*.git > index.html + # 1. Verify destination path exists + [ -d "$WEB_ROOT/$GIT_HTML" ] || { printf 'Error: %s/html not found\n' "$WEB_ROOT"; return 1; } + + # 2. Use a subshell to avoid persistent cd and ensure we run from the right place + ( + cd "$WEB_ROOT/$GIT_HTML" || exit 1 + + # 3. Check if we have repositories before running the command + # Use 'find' to safely handle the list of repos + repos=$(find "$SRC_DIR" -maxdepth 1 -name "*.git") + + if [ -n "$repos" ]; then + stagit-index $repos > index.html + else + printf 'No repositories found in %s\n' "$SRC_DIR" + fi + ) } process_index() { - [ -f "$DST_DIR/index.html" ] || return 0 - + [ -f "$WEB_ROOT/$GIT_HTML/index.html" ] || return 0 [ -n "${INDEX_TITLE:-}" ] && { esc_title=$(sed_escape "$INDEX_TITLE") - sed -i "s|<title>Repositories</title>|<title>$esc_title</title>|g" "$DST_DIR/index.html" \ - || warning "Failed to update index.html title" + sed -i "s|<title>Repositories</title>|<title>$esc_title</title>|g" "$WEB_ROOT/$GIT_HTML/index.html" } - [ -n "${INDEX_DESC:-}" ] && { esc_desc=$(sed_escape "$INDEX_DESC") - sed -i "s|<span class=\"desc\">Repositories</span>|<span class=\"desc\">$esc_desc</span>|g" \ - "$DST_DIR/index.html" || warning "Failed to update index.html description" + sed -i "s|<span class=\"desc\">Repositories</span>|<span class=\"desc\">$esc_desc</span>|g" "$WEB_ROOT/$GIT_HTML/index.html" } } deploy_assets() { - [ -d "$ASSETS_DIR" ] || return 0 - - # Copy assets to root - mkdir -p "$DST_DIR" + [ -d "${ASSETS_DIR:-}" ] || return 0 + mkdir -p "$TARGET_DIR" for f in "$ASSETS_DIR"/*; do - [ -f "$f" ] || continue - cp -L "$f" "$DST_DIR/$(basename "$f")" - done - - # Symlink assets into each repo dir - for repo_dir in "$DST_DIR"/*/log.html; do - [ -f "$repo_dir" ] || continue - repo_path="${repo_dir%/*}" - cd "$repo_path" - for f in "$ASSETS_DIR"/*; do - [ -f "$f" ] || continue - ln -sf "../$(basename "$f")" . - done - done -} - -load_config() { - cfg="$1" - [ -r "$cfg" ] || return 0 - - for key in SITE_NAME INDEX_TITLE INDEX_DESCRIPTION REPO_TITLE_DESCRIPTION; do - val=$(awk -F= -v k="$key" '$1 == k {print $2; exit}' "$cfg") || continue - [ -n "$val" ] || continue - case "$key" in - SITE_NAME) SITE_NAME="$val" ;; - INDEX_TITLE) INDEX_TITLE="$val" ;; - INDEX_DESCRIPTION) INDEX_DESC="$val" ;; - REPO_TITLE_DESCRIPTION) REPO_TITLE_DESC="$val" ;; - esac + [ -f "$f" ] && cp -L "$f" "$TARGET_DIR/$(basename "$f")" done } -# === MAIN LOGIC === +# --- Main --- main() { - CONFIG_FILE="" - CONFIG_FILE_SET=0 - - # Parse options - while getopts ":hc:" opt; do + CONFIG="$DEFAULT_CONFIG" + while getopts ":c:h" opt; do case "$opt" in - c) - CONFIG_FILE="$OPTARG" - CONFIG_FILE_SET=1 - ;; - h) - usage - exit 0 - ;; - :) error "option requires an argument: -$OPTARG" ;; - \?) error "invalid option: -$OPTARG" ;; + c) CONFIG="$OPTARG" ;; + h) printf 'usage: gitpages.sh [-c config] SRC\n'; exit 0 ;; + *) exit 1 ;; esac done shift $((OPTIND - 1)) - - check_args "$@" - SRC="$1" DST_DIR="$2" ASSETS_DIR="${3:-}" - SITE_NAME="" INDEX_TITLE="" INDEX_DESC="" REPO_TITLE_DESC="" - - # Validate prerequisites - check_html_writable && check_stagit && check_assets - - # Determine repo source - if is_git_repo "$SRC"; then - SRC_DIR="$(dirname "$SRC")" - SINGLE_REPO="$SRC" - else - SRC_DIR="$SRC" - SINGLE_REPO="" - has_git_repos "$SRC_DIR" || error "$SRC is neither git repo nor dir with *.git Repos" - fi - - # Load config - if [ "$CONFIG_FILE_SET" -eq 1 ]; then - [ -n "$CONFIG_FILE" ] || error "-c requires a non-empty argument" - [ -r "$CONFIG_FILE" ] || error "config file '$CONFIG_FILE' does not exist or is not readable" - else - CONFIG_FILE="$(dirname "$0")/$CONFIG_FILE_NAME" - fi - load_config "$CONFIG_FILE" - - # Unified repo processing - if [ -n "$SINGLE_REPO" ]; then - repo="$SINGLE_REPO" - repo_name=$(repo_base "$repo") - generate_repo_html "$repo" "$DST_DIR" "$repo_name" - process_repo_titles "$repo" "$repo_name" "$DST_DIR/$repo_name" - generate_index - info "Generated single repo: $repo → $DST_DIR" - else - # Clear destination (safety-checked earlier) - # shellcheck disable=SC2115 # wants non-posix compatible ${var:?} - [ "$DST_DIR" != "/" ] && rm -rf "$DST_DIR"/* - - for repo in "$SRC_DIR"/*.git; do - [ -e "$repo" ] || continue - is_git_repo "$repo" || { - info "Skipping $repo: not a git repository" - continue - } - - repo_name=$(repo_base "$repo") - generate_repo_html "$repo" "$DST_DIR" "$repo_name" - process_repo_titles "$repo" "$repo_name" "$DST_DIR/$repo_name" - done - generate_index - info "Generated all repos: $SRC → $DST_DIR" - fi - + [ $# -eq 1 ] || error "usage: gitpages.sh [-c config] SRC" + + # Load configuration + [ -r "$CONFIG" ] || error "config $CONFIG not readable" + WEB_ROOT=$(get_config_val "WEB_ROOT") + GIT_HTML=$(get_config_val "GIT_HTML") + GIT_RAW=$(get_config_val "GIT_RAW") + INDEX_TITLE=$(get_config_val "INDEX_TITLE") + INDEX_DESC=$(get_config_val "INDEX_DESCRIPTION") + REPO_TITLE_DESC=$(get_config_val "REPO_TITLE_DESCRIPTION") + ASSETS_DIR=$(get_config_val "ASSETS_DIR") + + # Setup environment context + check_deps + REPO_SRC="$1" + REPO_BASE=$(basename "$REPO_SRC" ".git") + SRC_DIR="$WEB_ROOT/$GIT_RAW" + TARGET_DIR="$WEB_ROOT/$GIT_HTML/$REPO_BASE" + + # Execution + generate_repo_html + process_repo_titles + generate_index process_index - [ -n "$ASSETS_DIR" ] && deploy_assets - - info "Generation complete" + deploy_assets + + info "Generation complete for $REPO_BASE" } main "$@" diff --git a/test-gitpages.sh b/test-gitpages.sh @@ -1,373 +1,54 @@ #!/bin/sh +set -e -set -eu - +# Configuration SCRIPT_NAME="./gitpages.sh" +TEST_DIR=$(realpath "$(mktemp -d)") -check_script() { - [ ! -x "$SCRIPT_NAME" ] && error "$SCRIPT_NAME not found or not executable" -} - -check_environment() { - command -v git > /dev/null 2>&1 || error "git not found" - command -v stagit > /dev/null 2>&1 || error "stagit not found" - command -v stagit-index > /dev/null 2>&1 || error "stagit-index not found" - command -v realpath > /dev/null 2>&1 || error "realpath not found" -} - -TEST_BASE="" +# Cleanup cleanup() { - [ -n "$TEST_BASE" ] && rm -rf "$TEST_BASE" -} -trap cleanup EXIT INT TERM - -error() { - printf 'FAIL: %s\n' "$1" >&2 - exit 1 -} - -success() { - printf 'PASS: %s\n' "$1" -} - -setup() { - TEST_BASE=$(mktemp -d) || error "Failed to create temp dir" - RAW_SRC="$TEST_BASE/raw-src" - HTML_DST="$TEST_BASE/html-dst" - mkdir -p "$RAW_SRC" "$HTML_DST" -} - -setup_assets() { - dst="$1" - mkdir -p "$dst" - printf 'icon\n' > "$dst/favicon.ico" - printf 'body {}\n' > "$dst/style.css" -} - -assert_fails() { - test_num="$1" - test_desc="$2" - shift 2 - printf '=== Test %s: %s ===\n' "$test_num" "$test_desc" - - if "$@" > /dev/null 2>&1; then - error "Should fail" - else - success "Rejects $test_desc" - fi -} - -assert_html_generated() { - html_dir="$1" - test_desc="$2" - - if [ ! -d "$html_dir" ]; then - error "$test_desc" - elif [ ! -f "$html_dir/log.html" ]; then - error "$test_desc" - else - success "$test_desc" - fi -} - -check_dir_exists() { - dir="$1" - msg="$2" - if [ -d "$dir" ]; then - success "$msg" - else - error "$msg" - fi -} - -check_dir_missing() { - dir="$1" - msg="$2" - if [ ! -d "$dir" ]; then - success "$msg" - else - error "$msg" - fi -} - -check_file_exists() { - file="$1" - msg="$2" - if [ -f "$file" ]; then - success "$msg" - else - error "$msg" - fi -} - -extract_title() { - file="$1" - title_line=$(grep -m1 '<title>' "$file" || true) - if [ -z "$title_line" ]; then - error "no <title> line in $file" - fi - printf '%s\n' "$title_line" | sed 's/^<title>//' | tr -d '\n' -} - -test_args() { - assert_fails 1 "wrong number of arguments" "$SCRIPT_NAME" "$RAW_SRC" -} - -test_bad_html_dst() { - bad_dst="$TEST_BASE/bad_dst" - touch "$bad_dst" - chmod 444 "$bad_dst" - assert_fails 2 "unwritable HTML dir" "$SCRIPT_NAME" "$RAW_SRC" "$bad_dst" -} - -test_empty_src() { - assert_fails 3 "empty source directory" "$SCRIPT_NAME" "$RAW_SRC" "$HTML_DST" -} - -test_single_repo() { - printf '=== Test 4: Single repo ===\n' - mkdir -p "$RAW_SRC/myrepo.git" - (cd "$RAW_SRC/myrepo.git" && git init --bare . > /dev/null 2>&1) - - "$SCRIPT_NAME" "$RAW_SRC/myrepo.git" "$HTML_DST" > /dev/null 2>&1 - - assert_html_generated "$HTML_DST/myrepo" "single repo HTML" -} - -test_full_sync() { - printf '=== Test 5: Full sync ===\n' - mkdir -p "$RAW_SRC/repo1.git" "$RAW_SRC/repo2" - (cd "$RAW_SRC/repo1.git" && git init --bare . > /dev/null 2>&1) - (cd "$RAW_SRC/repo2" && git init > /dev/null 2>&1) - - "$SCRIPT_NAME" "$RAW_SRC" "$HTML_DST" > /dev/null 2>&1 - - check_dir_exists "$HTML_DST/repo1" "repo1 HTML generated" - check_dir_missing "$HTML_DST/repo2" "non-bare repo2 skipped" -} - -test_stale_cleanup() { - printf '=== Test 6: Stale cleanup ===\n' - mkdir -p "$HTML_DST/stale-repo" - mkdir -p "$RAW_SRC/repo1.git" - (cd "$RAW_SRC/repo1.git" && git init --bare . > /dev/null 2>&1) - - "$SCRIPT_NAME" "$RAW_SRC" "$HTML_DST" > /dev/null 2>&1 - - check_dir_missing "$HTML_DST/stale-repo" "stale repo cleaned" - check_dir_exists "$HTML_DST/repo1" "new repo generated" -} - -test_assets_root_copy() { - printf '=== Test 7: Root assets copied ===\n' - - ASSETS_DIR="$TEST_BASE/assets" - setup_assets "$ASSETS_DIR" - - mkdir -p "$RAW_SRC/myrepo.git" - (cd "$RAW_SRC/myrepo.git" && git init --bare . > /dev/null 2>&1) - - "$SCRIPT_NAME" "$RAW_SRC/myrepo.git" "$HTML_DST" "$ASSETS_DIR" > /dev/null 2>&1 - - check_file_exists "$HTML_DST/favicon.ico" "asset favicon.ico copied to root" - check_file_exists "$HTML_DST/style.css" "asset style.css copied to root" -} - -test_assets_repo_symlinks() { - printf '=== Test 8: Asset symlinks in repo dir (self‑contained) ===\n' - - ASSETS_DIR="$TEST_BASE/assets-repo" - setup_assets "$ASSETS_DIR" - - mkdir -p "$ASSETS_DIR" "$RAW_SRC/myrepo.git" - (cd "$RAW_SRC/myrepo.git" && git init --bare . > /dev/null 2>&1) - - "$SCRIPT_NAME" "$RAW_SRC/myrepo.git" "$HTML_DST" "$ASSETS_DIR" > /dev/null 2>&1 - - myrepo_html="$HTML_DST/myrepo" - if [ -d "$myrepo_html" ] && [ -f "$myrepo_html/log.html" ]; then - if [ -L "$myrepo_html/favicon.ico" ] && [ -L "$myrepo_html/style.css" ]; then - success "asset symlinks created in repo dir (single repo)" - else - error "asset symlinks missing or not symlinks in repo dir (single repo)" - fi - else - error "repo HTML dir missing or no log.html" - fi -} - -test_full_sync_assets() { - printf '=== Test 9: Full sync with assets ===\n' - - ASSETS_DIR="$TEST_BASE/assets-full" - setup_assets "$ASSETS_DIR" - - mkdir -p "$ASSETS_DIR" "$RAW_SRC/repo1.git" "$RAW_SRC/repo2.git" - - (cd "$RAW_SRC/repo1.git" && git init --bare . > /dev/null 2>&1) - (cd "$RAW_SRC/repo2.git" && git init --bare . > /dev/null 2>&1) - - "$SCRIPT_NAME" "$RAW_SRC" "$HTML_DST" "$ASSETS_DIR" > /dev/null 2>&1 - - check_file_exists "$HTML_DST/favicon.ico" "asset favicon.ico in root (full sync)" - check_file_exists "$HTML_DST/style.css" "asset style.css in root (full sync)" - - for repo in "$HTML_DST"/repo*; do - [ -d "$repo" ] || continue - if [ -L "$repo/favicon.ico" ] && [ -L "$repo/style.css" ]; then - success "Repo assets ok in $repo" - else - error "asset symlinks missing in repo dir $repo" - fi - done -} - -test_repo_titles_without_rewriting() { - printf '=== Test 10a: Titles do NOT contain " - mycompany" (without rewriting) ===\n' - - mkdir -p "$RAW_SRC/myrepo.git" - (cd "$RAW_SRC/myrepo.git" && git init --bare . > /dev/null 2>&1) - - html_dst_no_c="$HTML_DST/no-mycompany" - mkdir -p "$html_dst_no_c" - - "$SCRIPT_NAME" "$RAW_SRC/myrepo.git" "$html_dst_no_c" > /dev/null 2>&1 || error "$SCRIPT_NAME failed without -c" - - myrepo_html_no_c="$html_dst_no_c/myrepo" - if [ ! -d "$myrepo_html_no_c" ] || [ ! -f "$myrepo_html_no_c/log.html" ]; then - error "repo HTML dir or log.html missing (no -c)" - fi - - find "$myrepo_html_no_c" -name "*.html" -type f | while IFS= read -r f; do - title=$(extract_title "$f") - if printf '%s\n' "$title" | grep -q ' - mycompany'; then - error "title in $f (without -c) contains ' - mycompany': '$title'" - fi - done - - success "Without -c, titles do not contain ' - mycompany'" -} - -test_repo_titles_rewriting() { - printf '=== Test 10b: Titles end with "X mycompany" (with rewriting) ===\n' - - mkdir -p "$RAW_SRC/demo.git" - (cd "$RAW_SRC/demo.git" && git init --bare . > /dev/null 2>&1) - - config_file="$TEST_BASE/gitpages.conf" - printf 'REPO_TITLE_DESCRIPTION=X mycompany\n' > "$config_file" - - html_dst_with_c="$HTML_DST/with-mycompany" - mkdir -p "$html_dst_with_c" - - "$SCRIPT_NAME" -c "$config_file" "$RAW_SRC/demo.git" "$html_dst_with_c" || error "$SCRIPT_NAME failed with -c" - - myrepo_html_with_c="$html_dst_with_c/demo" - if [ ! -d "$myrepo_html_with_c" ] || [ ! -f "$myrepo_html_with_c/log.html" ]; then - error "repo HTML dir or log.html missing (with -c)" - fi - - find "$myrepo_html_with_c" -name "*.html" -type f | while IFS= read -r f; do - title=$(extract_title "$f") - if printf '%s\n' "$title" | grep -q 'X mycompany$'; then - : # good - else - error "title in $f (with -c) does not end with 'X mycompany': '$title'" - fi - done - - success "With -c, titles end with 'X mycompany'" -} - -test_index_rewriting() { - printf '=== Test 11: Index title and description rewriting ===\n' - - mkdir -p "$RAW_SRC/site1.git" - (cd "$RAW_SRC/site1.git" && git init --bare . > /dev/null 2>&1) - - config_file="$TEST_BASE/gitweb-index.conf" - printf 'INDEX_TITLE=CustomIndex\nINDEX_DESCRIPTION=CustomDesc\n' > "$config_file" - - html_dst_idx="$HTML_DST/with-index" - mkdir -p "$html_dst_idx" - - "$SCRIPT_NAME" -c "$config_file" "$RAW_SRC" "$html_dst_idx" \ - || error "$SCRIPT_NAME failed with -c for index test" - - index_file="$html_dst_idx/index.html" - [ -f "$index_file" ] || error "index.html not generated" - - title_line=$(grep -m1 '<title>' "$index_file" || true) - desc_line=$(grep -m1 '<span class=\"desc\">' "$index_file" || true) - - if printf '%s\n' "$title_line" | grep -q '<title>CustomIndex</title>' && printf '%s\n' "$desc_line" | grep -q '<span class="desc">CustomDesc</span>'; then - success "index.html correctly rewrote title and description" - else - error "index.html did not rewrite title/description properly" - fi -} - -test_single_repo_does_not_remove_others() { - printf '=== Test 12: Single repo does not remove other repos ===\\n' - - # 1. Create two repos and do a full sync - mkdir -p "$RAW_SRC/repo1.git" "$RAW_SRC/repo2.git" - (cd "$RAW_SRC/repo1.git" && git init --bare > /dev/null 2>&1) - (cd "$RAW_SRC/repo2.git" && git init --bare > /dev/null 2>&1) - - "$SCRIPT_NAME" "$RAW_SRC" "$HTML_DST" > /dev/null 2>&1 \ - || error "$SCRIPT_NAME failed for full sync (before single‑repo test)" - - check_dir_exists "$HTML_DST/repo1" "repo1 HTML generated (before single run)" - check_dir_exists "$HTML_DST/repo2" "repo2 HTML generated (before single run)" - - # 2. Now run in single‑repo mode for repo1.git only - "$SCRIPT_NAME" "$RAW_SRC/repo1.git" "$HTML_DST" > /dev/null 2>&1 \ - || error "$SCRIPT_NAME failed for single repo (repo1.git)" - - # 3. Verify neither repo1 nor repo2 HTML dirs get deleted - check_dir_exists "$HTML_DST/repo1" "repo1 HTML still present after single‑repo run" - check_dir_exists "$HTML_DST/repo2" "repo2 HTML still present after single‑repo run" -} - -test_missing_or_empty_config_file() { - mkdir -p "$RAW_SRC/repo1.git" - (cd "$RAW_SRC/repo1.git" && git init --bare . > /dev/null 2>&1) - - missing_cfg="$TEST_BASE/does-not-exist.conf" - - # Missing file must fail. - assert_fails 13a "missing config file" \ - "$SCRIPT_NAME" -c "$missing_cfg" "$RAW_SRC/repo1.git" "$HTML_DST" - - # Empty path must also fail. - assert_fails 13b "empty config file path" \ - "$SCRIPT_NAME" -c "" "$RAW_SRC/repo1.git" "$HTML_DST" -} - -main() { - printf 'Testing %s\n' "$SCRIPT_NAME" - check_script - check_environment - setup - - test_args - test_bad_html_dst - test_empty_src - test_single_repo - test_full_sync - test_stale_cleanup - test_assets_root_copy - test_assets_repo_symlinks - test_full_sync_assets - test_repo_titles_without_rewriting - test_repo_titles_rewriting - test_index_rewriting - test_single_repo_does_not_remove_others - test_missing_or_empty_config_file - - printf '\nALL TESTS PASSED!\n' - printf 'Test environment cleaned up.\n' -} - -[ "${0##*/}" = "test-gitpages.sh" ] && main "$@" + rm -rf "$TEST_DIR" +} +trap cleanup EXIT + +# Prerequisites +[ -x "$SCRIPT_NAME" ] || { echo "ERROR: $SCRIPT_NAME missing"; exit 1; } +command -v stagit-index >/dev/null 2>&1 || { echo "ERROR: stagit-index missing"; +exit 1; } + +# Create Directory Structure expected by gitpages.sh +# Based on the trace, it expects: $WEB_ROOT/$GIT_RAW +WEB_ROOT="$TEST_DIR/public" +GIT_RAW_DIR="$WEB_ROOT/raw" +mkdir -p "$GIT_RAW_DIR" "$WEB_ROOT/html" "$TEST_DIR/assets" + +# Setup dummy repo in the location the script expects +REPO_NAME="myrepo" +REPO_DIR="$GIT_RAW_DIR/$REPO_NAME.git" +mkdir -p "$REPO_DIR" +(cd "$REPO_DIR" && git init --bare >/dev/null 2>&1) + +# Create config matching this structure +CONFIG_FILE="$TEST_DIR/gitpages.conf" +cat <<EOF > "$CONFIG_FILE" +WEB_ROOT=$WEB_ROOT +GIT_HTML=html +GIT_RAW=raw +ASSETS_DIR=$TEST_DIR/assets +EOF + +# Execution +echo "Running $SCRIPT_NAME on $REPO_DIR" +# We pass the repo directory. Ensure gitpages.sh handles absolute paths correctly. +"$SCRIPT_NAME" -c "$CONFIG_FILE" "$REPO_DIR" + +# Verification +# Stagit-index usually creates an index.html in the output directory +EXPECTED_OUTPUT="$WEB_ROOT/html/index.html" +if [ -f "$EXPECTED_OUTPUT" ]; then + echo "PASS: Generation successful" +else + echo "FAIL: $EXPECTED_OUTPUT not created" + ls -R "$TEST_DIR" + exit 1 +fi