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 db3578af44409c0eb51c21abcd5ab3cd45b22441
parent 318113482116bbbd68c837de3a73a3d5d3ac0b46
Author: Fred Großkopf <fred@kuandu.systems>
Date:   Thu, 17 Sep 2026 22:58:07 +0200

Cleans up script names

Diffstat:
Rgitpages-cmd.sh -> gitpages.sh | 0
Mtest-gitpages-generate.sh | 2+-
Dtest-gitpages-init-repos.sh | 104-------------------------------------------------------------------------------
Dtest-gitpages-mirror-git.sh | 156-------------------------------------------------------------------------------
Atest-gitpages-mirror.sh | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest-gitpages-process-queue.sh | 4++--
Atest-gitpages-setup.sh | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 235 insertions(+), 263 deletions(-)

diff --git a/gitpages-cmd.sh b/gitpages.sh diff --git a/test-gitpages-generate.sh b/test-gitpages-generate.sh @@ -3,7 +3,7 @@ set -eu SCRIPT_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd) -SCRIPT="$SCRIPT_DIR/gitpages-cmd.sh" +SCRIPT="$SCRIPT_DIR/gitpages.sh" TEST_ROOT=$(mktemp -d) trap 'rm -rf "$TEST_ROOT"' EXIT diff --git a/test-gitpages-init-repos.sh b/test-gitpages-init-repos.sh @@ -1,104 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2026 Fred Großkopf -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -set -eu - -WORKDIR="" -TARGET="" - -log() { printf 'test: %s\n' "$1"; } -die() { printf 'test: error: %s\n' "$1" >&2; exit 1; } - -setup_dirs() { - WORKDIR="$(mktemp -d -t gitpages_test.XXXXXX)" || - die "mktemp failed" - - TARGET="$WORKDIR/repos" - mkdir "$TARGET" - - # Quiet git default‑branch hint - GIT_CONFIG_NOSYSTEM=1 \ - git config --global init.defaultBranch main - - log "created test workspace in $WORKDIR" -} - -setup_repos() { - git init --bare "$TARGET/foo.git" - git init --bare "$TARGET/bar.git" - log "created foo.git and bar.git" -} - -run_gitpages_init() { - log "running gitpages-init-repos.sh" - - # Feed 3 lines per repo: desc, owner, url - ( - printf '%s\n' "Description for foo" - printf '%s\n' "user@foo.org" - printf '%s\n' "https://foo.example.com" - - printf '%s\n' "Description for bar" - printf '%s\n' "user@bar.org" - printf '%s\n' "https://bar.example.com" - ) | ./gitpages-cmd.sh setup "$TARGET" -} - -inspect_repos() { - log "contents of repos:" - for repo in "$TARGET"/*.git; do - if [ -d "$repo" ]; then - repo_name=$(basename "$repo" .git) - printf '\n== %s ==\n' "$repo_name" - - if [ -f "$repo/description" ]; then - printf 'desc:\n'; cat "$repo/description" - else - printf 'desc: (MISSING!)\n' - fi - - if [ -f "$repo/owner" ]; then - printf '\nowner:\n'; cat "$repo/owner" - else - printf 'owner: (MISSING!)\n' - fi - - if [ -f "$repo/url" ]; then - printf '\nurl:\n'; cat "$repo/url" - else - printf 'url: (MISSING!)\n' - fi - - hook="$repo/hooks/post-receive" - if [ -f "$hook" ]; then - printf '\npost-receive (head 10):\n'; head -10 "$hook" - else - printf 'post-receive hook: (MISSING!)\n' - fi - fi - done -} - -main() { - setup_dirs - setup_repos - run_gitpages_init - inspect_repos - - log "test complete" -} - -main diff --git a/test-gitpages-mirror-git.sh b/test-gitpages-mirror-git.sh @@ -1,156 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2026 Fred Großkopf -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -set -eu - -SCRIPT_NAME="./gitpages-cmd.sh" - -check_script() { - if [ ! -x "$SCRIPT_NAME" ]; then - printf 'ERROR: %s not found or not executable\n' "$SCRIPT_NAME" >&2 - printf 'Please run:\n chmod +x git-sync.sh\n ./test-git-sync.sh\n' >&2 - exit 1 - fi -} - -TEST_BASE="" -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" - SRC_DIR="$TEST_BASE/src" - DST_DIR="$TEST_BASE/dst" - mkdir -p "$SRC_DIR" "$DST_DIR" -} - -assert_fails() { - test_num="$1" - test_desc="$2" - shift 2 - printf '=== Test %s: %s ===\n' "$test_num" "$test_desc" - "$@" && error "Should fail" || success "Rejects $test_desc" -} - -assert_repo_copied() { - repo="$1" - test_name="$2" - [ -d "$repo" ] && success "$test_name" || error "$test_name" -} - -assert_repo_skipped() { - repo="$1" - test_name="$2" - [ ! -d "$repo" ] && success "$test_name" || error "$test_name" -} - -assert_bare_repo() { - repo="$1" - test_name="$2" - git --git-dir="$repo" rev-parse --is-bare-repository >/dev/null 2>&1 || error "$test_name" -} - -assert_server_info() { - repo="$1" - test_name="$2" - [ -f "$repo/info/refs" ] && success "$test_name" || error "$test_name" -} - -test_args() { - assert_fails 1 "Wrong number of arguments" "$SCRIPT_NAME" "mirror" "$SRC_DIR" "$DST_DIR" extra_arg -} - -test_bad_src_file() { - bad_src="$TEST_BASE/bad_src" - touch "$bad_src" - assert_fails 2 "Source is file" "$SCRIPT_NAME" "mirror" "$bad_src" "$DST_DIR" -} - -test_bad_dst() { - bad_dst="$TEST_BASE/bad_dst" - touch "$bad_dst" && chmod 444 "$bad_dst" - assert_fails 3 "Destination not writable" "$SCRIPT_NAME" "mirror" "$SRC_DIR" "$bad_dst" -} - -test_empty_src() { - rm -rf "$SRC_DIR"/* - assert_fails 4 "Empty source directory" "$SCRIPT_NAME" "mirror" "$SRC_DIR" "$DST_DIR" -} - -test_full_sync() { - printf '=== Test 5: Full sync ===\n' - mkdir -p "$SRC_DIR/myrepo.git" "$SRC_DIR/otherrepo" - (cd "$SRC_DIR/myrepo.git" && git init --bare . >/dev/null 2>&1) - (cd "$SRC_DIR/otherrepo" && git init >/dev/null 2>&1) - - "$SCRIPT_NAME" mirror "$SRC_DIR" "$DST_DIR" - - assert_repo_copied "$DST_DIR/myrepo.git" "Bare repo copied" - assert_repo_skipped "$DST_DIR/otherrepo" "Skipped non-bare" - assert_bare_repo "$DST_DIR/myrepo.git" "Destination is bare" - assert_server_info "$DST_DIR/myrepo.git" "update-server-info ran" -} - -test_single_repo() { - printf '=== Test 6: Single repo ===\n' - mkdir -p "$SRC_DIR/myrepo.git" - (cd "$SRC_DIR/myrepo.git" && git init --bare . >/dev/null 2>&1) - - "$SCRIPT_NAME" mirror "$SRC_DIR/myrepo.git" "$DST_DIR" - - assert_repo_copied "$DST_DIR/myrepo.git" "Single repo copied" - assert_bare_repo "$DST_DIR/myrepo.git" "Single mode: bare repo" - assert_server_info "$DST_DIR/myrepo.git" "Single mode: update-server-info ran" -} - -test_root_protection() { - assert_fails 7 "Root protection" "$SCRIPT_NAME" "mirror" "$SRC_DIR" "/" -} - -test_non_repo_dir() { - assert_fails 8 "Non-repo directory" "$SCRIPT_NAME" "mirror" "$TEST_BASE" "$DST_DIR" -} - -main() { - printf 'Testing %s\n' "$SCRIPT_NAME mirror" - check_script - setup - - test_args - test_bad_src_file - test_bad_dst - test_empty_src - test_full_sync - test_single_repo - test_root_protection - test_non_repo_dir - - printf '\nALL TESTS PASSED!\n' - printf 'Test environment cleaned up.\n' -} - -[ "${0##*/}" = "test-gitpages-mirror-git.sh" ] && main "$@" diff --git a/test-gitpages-mirror.sh b/test-gitpages-mirror.sh @@ -0,0 +1,142 @@ +#!/bin/sh + +set -eu + +SCRIPT_NAME="./gitpages.sh" + +check_script() { + if [ ! -x "$SCRIPT_NAME" ]; then + printf 'ERROR: %s not found or not executable\n' "$SCRIPT_NAME" >&2 + printf 'Please run:\n chmod +x git-sync.sh\n ./test-git-sync.sh\n' >&2 + exit 1 + fi +} + +TEST_BASE="" +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" + SRC_DIR="$TEST_BASE/src" + DST_DIR="$TEST_BASE/dst" + mkdir -p "$SRC_DIR" "$DST_DIR" +} + +assert_fails() { + test_num="$1" + test_desc="$2" + shift 2 + printf '=== Test %s: %s ===\n' "$test_num" "$test_desc" + "$@" && error "Should fail" || success "Rejects $test_desc" +} + +assert_repo_copied() { + repo="$1" + test_name="$2" + [ -d "$repo" ] && success "$test_name" || error "$test_name" +} + +assert_repo_skipped() { + repo="$1" + test_name="$2" + [ ! -d "$repo" ] && success "$test_name" || error "$test_name" +} + +assert_bare_repo() { + repo="$1" + test_name="$2" + git --git-dir="$repo" rev-parse --is-bare-repository >/dev/null 2>&1 || error "$test_name" +} + +assert_server_info() { + repo="$1" + test_name="$2" + [ -f "$repo/info/refs" ] && success "$test_name" || error "$test_name" +} + +test_args() { + assert_fails 1 "Wrong number of arguments" "$SCRIPT_NAME" "mirror" "$SRC_DIR" "$DST_DIR" extra_arg +} + +test_bad_src_file() { + bad_src="$TEST_BASE/bad_src" + touch "$bad_src" + assert_fails 2 "Source is file" "$SCRIPT_NAME" "mirror" "$bad_src" "$DST_DIR" +} + +test_bad_dst() { + bad_dst="$TEST_BASE/bad_dst" + touch "$bad_dst" && chmod 444 "$bad_dst" + assert_fails 3 "Destination not writable" "$SCRIPT_NAME" "mirror" "$SRC_DIR" "$bad_dst" +} + +test_empty_src() { + rm -rf "$SRC_DIR"/* + assert_fails 4 "Empty source directory" "$SCRIPT_NAME" "mirror" "$SRC_DIR" "$DST_DIR" +} + +test_full_sync() { + printf '=== Test 5: Full sync ===\n' + mkdir -p "$SRC_DIR/myrepo.git" "$SRC_DIR/otherrepo" + (cd "$SRC_DIR/myrepo.git" && git init --bare . >/dev/null 2>&1) + (cd "$SRC_DIR/otherrepo" && git init >/dev/null 2>&1) + + "$SCRIPT_NAME" mirror "$SRC_DIR" "$DST_DIR" + + assert_repo_copied "$DST_DIR/myrepo.git" "Bare repo copied" + assert_repo_skipped "$DST_DIR/otherrepo" "Skipped non-bare" + assert_bare_repo "$DST_DIR/myrepo.git" "Destination is bare" + assert_server_info "$DST_DIR/myrepo.git" "update-server-info ran" +} + +test_single_repo() { + printf '=== Test 6: Single repo ===\n' + mkdir -p "$SRC_DIR/myrepo.git" + (cd "$SRC_DIR/myrepo.git" && git init --bare . >/dev/null 2>&1) + + "$SCRIPT_NAME" mirror "$SRC_DIR/myrepo.git" "$DST_DIR" + + assert_repo_copied "$DST_DIR/myrepo.git" "Single repo copied" + assert_bare_repo "$DST_DIR/myrepo.git" "Single mode: bare repo" + assert_server_info "$DST_DIR/myrepo.git" "Single mode: update-server-info ran" +} + +test_root_protection() { + assert_fails 7 "Root protection" "$SCRIPT_NAME" "mirror" "$SRC_DIR" "/" +} + +test_non_repo_dir() { + assert_fails 8 "Non-repo directory" "$SCRIPT_NAME" "mirror" "$TEST_BASE" "$DST_DIR" +} + +main() { + printf 'Testing %s\n' "$SCRIPT_NAME mirror" + check_script + setup + + test_args + test_bad_src_file + test_bad_dst + test_empty_src + test_full_sync + test_single_repo + test_root_protection + test_non_repo_dir + + printf '\nALL TESTS PASSED!\n' + printf 'Test environment cleaned up.\n' +} + +[ "${0##*/}" = "test-gitpages-mirror.sh" ] && main "$@" diff --git a/test-gitpages-process-queue.sh b/test-gitpages-process-queue.sh @@ -66,8 +66,8 @@ GIT_RAW=$GIT_RAW GIT_HTML=$GIT_HTML EOF -echo "Running test on gitpages-cmd.sh..." -./gitpages-cmd.sh process_queue -c "$CONFIG_FILE" +echo "Running test on gitpages.sh..." +./gitpages.sh process_queue -c "$CONFIG_FILE" # Verify: queue directory should be empty (no job files left) files=$(find "$GITPAGES_QUEUE_DIR" -type f | wc -l) diff --git a/test-gitpages-setup.sh b/test-gitpages-setup.sh @@ -0,0 +1,90 @@ +#!/bin/sh + +set -eu + +WORKDIR="" +TARGET="" + +log() { printf 'test: %s\n' "$1"; } +die() { printf 'test: error: %s\n' "$1" >&2; exit 1; } + +setup_dirs() { + WORKDIR="$(mktemp -d -t gitpages_test.XXXXXX)" || + die "mktemp failed" + + TARGET="$WORKDIR/repos" + mkdir "$TARGET" + + # Quiet git default‑branch hint + GIT_CONFIG_NOSYSTEM=1 \ + git config --global init.defaultBranch main + + log "created test workspace in $WORKDIR" +} + +setup_repos() { + git init --bare "$TARGET/foo.git" + git init --bare "$TARGET/bar.git" + log "created foo.git and bar.git" +} + +run_gitpages_init() { + log "running gitpages-init-repos.sh" + + # Feed 3 lines per repo: desc, owner, url + ( + printf '%s\n' "Description for foo" + printf '%s\n' "user@foo.org" + printf '%s\n' "https://foo.example.com" + + printf '%s\n' "Description for bar" + printf '%s\n' "user@bar.org" + printf '%s\n' "https://bar.example.com" + ) | ./gitpages.sh setup "$TARGET" +} + +inspect_repos() { + log "contents of repos:" + for repo in "$TARGET"/*.git; do + if [ -d "$repo" ]; then + repo_name=$(basename "$repo" .git) + printf '\n== %s ==\n' "$repo_name" + + if [ -f "$repo/description" ]; then + printf 'desc:\n'; cat "$repo/description" + else + printf 'desc: (MISSING!)\n' + fi + + if [ -f "$repo/owner" ]; then + printf '\nowner:\n'; cat "$repo/owner" + else + printf 'owner: (MISSING!)\n' + fi + + if [ -f "$repo/url" ]; then + printf '\nurl:\n'; cat "$repo/url" + else + printf 'url: (MISSING!)\n' + fi + + hook="$repo/hooks/post-receive" + if [ -f "$hook" ]; then + printf '\npost-receive (head 10):\n'; head -10 "$hook" + else + printf 'post-receive hook: (MISSING!)\n' + fi + fi + done +} + +main() { + setup_dirs + setup_repos + run_gitpages_init + inspect_repos + + log "test complete" +} + +main