commit e3cf35ff4b002e699d5cdb01b89ecf072ffb6468
parent ac78252135f2c65c6bfb41d32f6d8f465084dced
Author: Fred Großkopf <fred@kuandu.systems>
Date: Thu, 30 Apr 2026 17:07:14 +0200
Use /var/spool/gitpages/queue
to ensure post-receive hooks
and gitpages-update don't interfere.
Diffstat:
5 files changed, 160 insertions(+), 63 deletions(-)
diff --git a/gitpages-update.sh b/gitpages-update.sh
@@ -6,7 +6,7 @@ PATH=/usr/local/bin:/usr/bin:/bin:$PATH
# --- Configuration ---
LOCK_DIR="${GITPAGES_LOCK_DIR:-/tmp/gitpages-update.lock}"
-QUEUE_FILE="${GITPAGES_QUEUE_FILE:-/var/spool/gitpages-update-queue}"
+QUEUE_DIR="${GITPAGES_QUEUE_DIR:-/var/spool/gitpages/queue}"
LOG_FILE="${GITPAGES_LOG_FILE:-/var/log/gitpages/update.log}"
DEFAULT_CONFIG="/etc/gitpages.conf"
@@ -55,20 +55,30 @@ main() {
fi
trap 'rmdir "$LOCK_DIR"' EXIT
- if [ -f "$QUEUE_FILE" ] && [ -s "$QUEUE_FILE" ]; then
- paths=$(sort -u "$QUEUE_FILE")
- : > "$QUEUE_FILE"
+ if [ ! -d "$QUEUE_DIR" ]; then
+ log "Queue directory not found: $QUEUE_DIR"
+ return 0
+ fi
+
+ for jobfile in "$QUEUE_DIR"/*; do
+ [ -f "$jobfile" ] || continue
+
+ repo_path=$(cat "$jobfile")
- log "Starting update for: $paths"
- for repo_path in $paths; do
- if [ -d "$repo_path" ]; then
- process_repo "$repo_path" "" "$CONFIG_FILE"
+ if [ -d "$repo_path" ]; then
+ log "Starting update for: $repo_path (job: $jobfile)"
+ if process_repo "$repo_path" "" "$CONFIG_FILE"; then
+ rm -f "$jobfile"
else
- error "Repository directory $repo_path not found"
+ error "Failed to process $repo_path (job: $jobfile)"
fi
- done
- log "Update completed."
- fi
+ else
+ error "Repository directory $repo_path not found (job: $jobfile)"
+ fi
+ done
+
+ log "Update completed."
+
}
mkdir -p "$(dirname "$LOG_FILE")"
diff --git a/install.sh b/install.sh
@@ -14,18 +14,36 @@ usage() {
}
setup_dirs() {
- install -d -m 755 -o gitpages -g gitpages "$VAR_DIR" "$LOG_DIR"
+ install -d -m 755 -o "$USER_NAME" -g "$GROUP_NAME" "$VAR_DIR" "$LOG_DIR"
install -d -m 755 "$BIN_DIR" "$SPOOL_DIR"
}
+setup_group() {
+ if ! groupinfo "$GROUP_NAME" > /dev/null 2>&1; then
+ if command -v groupadd > /dev/null 2>&1; then
+ groupadd "$GROUP_NAME"
+ log "Created group $GROUP_NAME"
+ else
+ error "Neither groupinfo nor groupadd found; cannot create group $GROUP_NAME. Please create it manually."
+ fi
+ fi
+}
+
setup_user() {
- if ! id "gitpages" > /dev/null 2>&1; then
- useradd -s /sbin/nologin -d "$VAR_DIR" -c "Gitpages service user" gitpages
+ setup_group
+
+ if ! id "$USER_NAME" > /dev/null 2>&1; then
+ useradd -s /sbin/nologin \
+ -d "$VAR_DIR" \
+ -c "Gitpages service user" \
+ -g "$GROUP_NAME" \
+ "$USER_NAME"
+ log "Created user $USER_NAME (primary group: $GROUP_NAME)"
fi
}
setup_spool() {
- install -m 664 -o "$GIT_USER" -g gitpages /dev/null "$QUEUE_FILE"
+ install -d -m 2775 -o "$GIT_USER" -g "$GROUP_NAME" "$QUEUE_DIR"
}
install_scripts() {
@@ -46,6 +64,7 @@ GIT_SRC=/var/git/repos
WEB_ROOT=/var/www/htdocs
GIT_RAW=git-raw
GIT_HTML=git
+ASSETS_DIR=/var/gitpages/assets
EOF
log "Created default config in $ETC_DIR/gitpages.conf"
fi
@@ -53,7 +72,7 @@ EOF
setup_logrotation() {
if ! grep -q "$LOG_DIR/updates.log" "$ETC_DIR"/newsyslog.conf; then
- echo "$LOG_DIR/updates.log gitpages:gitpages 640 5 100 * J" >> "$ETC_DIR"/newsyslog.conf
+ echo "$LOG_DIR/updates.log ${USER_NAME}:${GROUP_NAME} 640 5 100 * J" >> "$ETC_DIR"/newsyslog.conf
log "Log rotation appended to $ETC_DIR/newsyslog.conf."
else
log "Log rotation already configured in $ETC_DIR/newsyslog.conf."
@@ -67,11 +86,10 @@ setup_cron() {
error "Cannot setup cron: $BIN_DIR/gitpages-update.sh not found."
fi
- if crontab -u gitpages -l 2> /dev/null | grep -qF "$cron_line"; then
+ if crontab -u "$USER_NAME" -l 2> /dev/null | grep -qF "$cron_line"; then
log "Cronjob already exists and is configured correctly."
else
- # Just echo the line into the crontab command
- echo "$cron_line" | crontab -u gitpages -
+ echo "$cron_line" | crontab -u "$USER_NAME" -
log "Cronjob set for gitpages user."
fi
}
@@ -79,13 +97,15 @@ setup_cron() {
main() {
[ $# -ne 1 ] && usage
GIT_USER="$1"
+ USER_NAME="_gitpages"
+ GROUP_NAME="gitpages"
BIN_DIR="/usr/local/sbin"
ETC_DIR="/etc"
VAR_DIR="/var/gitpages"
LOG_DIR="/var/log/gitpages"
SPOOL_DIR="/var/spool"
- QUEUE_FILE="$SPOOL_DIR/gitpages-update-queue"
+ QUEUE_DIR="$SPOOL_DIR/gitpages/queue"
if ! id "$GIT_USER" > /dev/null 2>&1; then
error "User '$GIT_USER' does not exist."
diff --git a/post-receive.hook b/post-receive.hook
@@ -1,8 +1,52 @@
#!/bin/sh
-#
-# Place this in: /var/git/repos/<repo>.git/hooks/post-receive
-QUEUE_FILE="/var/spool/gitpages-update-queue"
+set -euf
-# Append the full path of the current directory to the queue
-echo "$PWD" >> "$QUEUE_FILE"
+QUEUE_DIR="/var/spool/gitpages/queue"
+
+sanitize_repo_name() {
+ name="$1"
+
+ [ -z "$name" ] && name="unknown"
+
+ # Keep only printable chars, strip leading/trailing ./
+ name=$(printf '%s\n' "$name" \
+ | tr -cd '[:print:]\n' \
+ | sed 's|^[./]*||; s|[./]*$||')
+
+ # Keep only safe chars, replace others with -
+ name=$(printf '%s\n' "$name" \
+ | sed 's|[^A-Za-z0-9._-]|-|g')
+
+ # Collapse multiple - or . sequences
+ name=$(printf '%s\n' "$name" \
+ | sed 's|[-.]\{2,\}|-|g')
+
+ # Final sanity fall<E2><80><91>back
+ case "$name" in
+ '' | '.' | '..') name="invalid-repo" ;;
+ esac
+
+ printf '%s\n' "$name"
+}
+
+main() {
+ REPO_PATH="$PWD"
+
+ # Extract repo name from path (e.g., "A" from /var/git/A.git)
+ repo_name=$(basename "$REPO_PATH" .git)
+
+ # Sanitize repo name for safe filename
+ safe_repo_name=$(sanitize_repo_name "$repo_name")
+
+ # Queue job file: /var/spool/gitpages/queue/<safe_repo_name>
+ jobfile="$QUEUE_DIR/$safe_repo_name"
+
+ umask 002
+
+ # Write repo_path into the job file, atomically
+ printf '%s\n' "$REPO_PATH" > "$jobfile.$$"
+ mv "$jobfile.$$" "$jobfile"
+}
+
+main
diff --git a/test-gitpages-update.sh b/test-gitpages-update.sh
@@ -1,11 +1,14 @@
#!/bin/sh
+
set -e
# Configuration
TEST_DIR=$(realpath "$(mktemp -d)")
+
export GITPAGES_LOCK_DIR="$TEST_DIR/lock"
-export GITPAGES_QUEUE_FILE="$TEST_DIR/queue.txt"
+export GITPAGES_QUEUE_DIR="$TEST_DIR/queue"
export GITPAGES_LOG_FILE="$TEST_DIR/log.txt"
+
CONFIG_FILE="$TEST_DIR/gitpages.conf"
GIT_RAW="$TEST_DIR/raw"
REPO_PATH="$GIT_RAW/myrepo.git"
@@ -17,7 +20,7 @@ export PATH="$TEST_DIR/bin:$PATH"
mock_bin() {
cat << EOF > "$TEST_DIR/bin/$1"
#!/bin/sh
-echo "MOCK: $1 called with \$*"
+printf "MOCK: %s called with \$*\n" "$1" >&2
exit 0
EOF
chmod +x "$TEST_DIR/bin/$1"
@@ -28,8 +31,11 @@ mock_bin gitpages.sh
# Setup test environment
mkdir -p "$GIT_RAW"
-touch "$REPO_PATH"
-echo "$REPO_PATH" > "$GITPAGES_QUEUE_FILE"
+mkdir -p "$GITPAGES_QUEUE_DIR"
+mkdir -p "$REPO_PATH"
+
+# Create a job file whose content is the repo path
+printf '%s\n' "$REPO_PATH" > "$GITPAGES_QUEUE_DIR/myrepo"
# Create test config
cat << EOF > "$CONFIG_FILE"
@@ -39,11 +45,13 @@ EOF
echo "Running test on gitpages-update.sh..."
./gitpages-update.sh -c "$CONFIG_FILE"
-# Verify
-if [ ! -s "$GITPAGES_QUEUE_FILE" ]; then
- echo "PASS: Queue was cleared."
+# Verify: queue directory should be empty (no job files left)
+files=$(find "$GITPAGES_QUEUE_DIR" -type f | wc -l)
+if [ "$files" -eq 0 ]; then
+ echo "PASS: Queue directory is empty."
else
- echo "FAIL: Queue was not cleared."
+ echo "FAIL: Queue directory is not empty (has $files files)."
+ find "$GITPAGES_QUEUE_DIR" -type f
exit 1
fi
diff --git a/uninstall.sh b/uninstall.sh
@@ -8,50 +8,65 @@ BIN_DIR="/usr/local/sbin"
ETC_DIR="/etc"
VAR_DIR="/var/gitpages"
LOG_DIR="/var/log/gitpages"
+USER_NAME="_gitpages"
+GROUP_NAME="gitpages"
remove_cron() {
- if id "gitpages" >/dev/null 2>&1; then
- crontab -u gitpages -r 2>/dev/null || true
- log "Removed crontab for gitpages user."
- fi
+ if id "$USER_NAME" > /dev/null 2>&1; then
+ crontab -u "$USER_NAME" -r 2> /dev/null || true
+ log "Removed crontab for ${USER_NAME} user."
+ fi
}
remove_files() {
- rm -f "$ETC_DIR/gitpages.conf"
- rm -f "$BIN_DIR/gitpages.sh" "$BIN_DIR/gitpages-init.sh" \
- "$BIN_DIR/gitpages-mirror-git.sh" "$BIN_DIR/gitpages-update.sh"
- log "Removed configuration and binaries."
+ rm -f "$ETC_DIR/gitpages.conf"
+ rm -f "$BIN_DIR/gitpages.sh" "$BIN_DIR/gitpages-init.sh" \
+ "$BIN_DIR/gitpages-mirror-git.sh" "$BIN_DIR/gitpages-update.sh"
+ log "Removed configuration and binaries."
}
remove_logrotation() {
- if [ -f "$ETC_DIR/newsyslog.conf" ]; then
- # Use a different delimiter for sed since the path contains slashes
- sed -i "\%$LOG_DIR/updates.log%d" "$ETC_DIR/newsyslog.conf"
- log "Removed log rotation entry."
- fi
+ if [ -f "$ETC_DIR/newsyslog.conf" ]; then
+ # Use a different delimiter for sed since the path contains slashes
+ sed -i "\%$LOG_DIR/updates.log%d" "$ETC_DIR/newsyslog.conf"
+ log "Removed log rotation entry."
+ fi
}
remove_user_and_data() {
- if id "gitpages" >/dev/null 2>&1; then
- userdel gitpages
- log "Removed gitpages user."
- fi
-
- if [ -d "$VAR_DIR" ]; then
- rm -rf "$VAR_DIR"
- log "Removed service directory $VAR_DIR."
+ if id "$USER_NAME" > /dev/null 2>&1; then
+ userdel "$USER_NAME"
+ log "Removed ${USER_NAME} user."
+ fi
+
+ if [ -d "$VAR_DIR" ]; then
+ rm -rf "$VAR_DIR"
+ log "Removed service directory $VAR_DIR."
+ fi
+}
+
+remove_group() {
+ if groupinfo "$GROUP_NAME" > /dev/null 2>&1; then
+ if command -v groupdel > /dev/null 2>&1; then
+ groupdel "$GROUP_NAME"
+ log "Removed group $GROUP_NAME"
+ else
+ # On some systems groupdel may not exist; fall back to manual /etc/group edit
+ log "WARNING: groupdel not found; please remove group $GROUP_NAME manually from /etc/group."
fi
+ fi
}
main() {
- log "Starting uninstallation of gitpages..."
-
- remove_cron
- remove_files
- remove_logrotation
- remove_user_and_data
-
- log "Uninstallation complete."
+ log "Starting uninstallation of gitpages..."
+
+ remove_cron
+ remove_files
+ remove_logrotation
+ remove_user_and_data
+ remove_group
+
+ log "Uninstallation complete."
}
main "$@"