From 29682e821f1c4dadc7560480cdd18aa670f983db Mon Sep 17 00:00:00 2001 From: Ark74 Date: Thu, 5 Mar 2026 20:09:02 -0600 Subject: [PATCH] [simple] add initial simple repo builder --- scripts/simple_builder.sh | 177 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 scripts/simple_builder.sh diff --git a/scripts/simple_builder.sh b/scripts/simple_builder.sh new file mode 100644 index 0000000..a4f1c2e --- /dev/null +++ b/scripts/simple_builder.sh @@ -0,0 +1,177 @@ +#!/usr/bin/env bash +set -euo pipefail + +# gen_simple_inplace.sh +# Generate PEP 503 "simple" indexes in-place for existing directories: +# //index.html +# Optionally generates: +# /index.html + +die() { echo "ERROR: $*" >&2; exit 1; } + +SIMPLE_DIR="" +ONLY_PKG="" +NO_TOP=0 +DO_VERIFY=0 +VERIFY_ONLY=0 + +while [ $# -gt 0 ]; do + case "$1" in + --simple-dir) SIMPLE_DIR="${2:-}"; shift 2 ;; + --pkg) ONLY_PKG="${2:-}"; shift 2 ;; + --no-top) NO_TOP=1; shift ;; + --verify) DO_VERIFY=1; shift ;; + --verify-only) VERIFY_ONLY=1; DO_VERIFY=1; shift ;; + -h|--help) + cat <<'EOF' +Usage: + ./gen_simple_inplace.sh --simple-dir /var/www/.../simple + ./gen_simple_inplace.sh --simple-dir /var/www/.../simple --pkg cffi --no-top + ./gen_simple_inplace.sh --simple-dir /var/www/.../simple --verify + ./gen_simple_inplace.sh --simple-dir /var/www/.../simple --verify-only +Options: + --no-top Don't rewrite /simple/index.html + --verify Verify that each /index.html href exists and sha256 matches + --verify-only Verify only (do not regenerate any index.html) +EOF + exit 0 + ;; + *) die "Unknown arg: $1" ;; + esac +done + +[ -n "$SIMPLE_DIR" ] || die "--simple-dir is required" +[ -d "$SIMPLE_DIR" ] || die "Not a directory: $SIMPLE_DIR" + +is_artifact() { + case "$1" in + *.whl|*.tar.gz|*.zip|*.tgz) return 0 ;; + *) return 1 ;; + esac +} + +verify_pkg_index() { + local pkgdir="$1" + local pkgname="$2" + local idx="$pkgdir/index.html" + local errs=0 + + if [ ! -f "$idx" ]; then + echo "VERIFY FAIL [$pkgname]: missing index.html" >&2 + return 1 + fi + + # Extract href targets like: filename#sha256=.... + # We assume filenames don't contain quotes/spaces (true for wheels/sdists typically). + while IFS= read -r href; do + [ -n "$href" ] || continue + local file="${href%%#sha256=*}" + local want="${href##*#sha256=}" + + if [ ! -f "$pkgdir/$file" ]; then + echo "VERIFY FAIL [$pkgname]: missing file: $file" >&2 + errs=$((errs+1)) + continue + fi + + local got + got="$(sha256sum "$pkgdir/$file" | awk '{print $1}')" + if [ "$got" != "$want" ]; then + echo "VERIFY FAIL [$pkgname]: sha256 mismatch for $file" >&2 + echo " want: $want" >&2 + echo " got : $got" >&2 + errs=$((errs+1)) + fi + done < <( + # Pull href="..."; keep only those containing #sha256= + grep -oE 'href="[^"]+"' "$idx" \ + | sed -E 's/^href="(.*)"$/\1/' \ + | grep -E '#sha256=' || true + ) + + if [ "$errs" -gt 0 ]; then + echo "VERIFY FAIL [$pkgname]: $errs error(s)" >&2 + return 1 + fi + + echo "VERIFY OK [$pkgname]" + return 0 +} + +write_pkg_index() { + local pkgdir="$1" + local pkgname="$2" + local idx="$pkgdir/index.html" + + # Collect artifacts in that pkg dir + mapfile -t files < <(find "$pkgdir" -maxdepth 1 -type f \( -name '*.whl' -o -name '*.tar.gz' -o -name '*.zip' -o -name '*.tgz' \) -printf '%f\n' | sort) + + # If no artifacts, skip (or you can still write an empty index if you want) + [ "${#files[@]}" -gt 0 ] || return 0 + + { + echo "" + echo "${pkgname}" + for bn in "${files[@]}"; do + # hash the file in place + sha="$(sha256sum "$pkgdir/$bn" | awk '{print $1}')" + printf '%s
\n' "$bn" "$sha" "$bn" + done + echo "" + } > "$idx" +} + +# Determine package dirs +pkg_dirs=() +if [ -n "$ONLY_PKG" ]; then + [ -d "$SIMPLE_DIR/$ONLY_PKG" ] || die "Package dir not found: $SIMPLE_DIR/$ONLY_PKG" + pkg_dirs+=("$SIMPLE_DIR/$ONLY_PKG") +else + # All subdirs except hidden ones + while IFS= read -r d; do + pkg_dirs+=("$d") + done < <(find "$SIMPLE_DIR" -mindepth 1 -maxdepth 1 -type d ! -name '.*' | sort) +fi + +if [ "$VERIFY_ONLY" -eq 0 ]; then + # Generate per-package indexes + for d in "${pkg_dirs[@]}"; do + pkg="$(basename "$d")" + write_pkg_index "$d" "$pkg" + done + + # Top index (optional) + if [ "$NO_TOP" -eq 0 ] && [ -z "$ONLY_PKG" ]; then + top="$SIMPLE_DIR/index.html" + { + echo "" + echo "Simple Index" + for d in "${pkg_dirs[@]}"; do + pkg="$(basename "$d")" + if find "$d" -maxdepth 1 -type f \( -name '*.whl' -o -name '*.tar.gz' -o -name '*.zip' -o -name '*.tgz' \) | grep -q .; then + printf '%s
\n' "$pkg" "$pkg" + fi + done + echo "" + } > "$top" + fi +fi + +if [ "$DO_VERIFY" -eq 1 ]; then + vfail=0 + for d in "${pkg_dirs[@]}"; do + pkg="$(basename "$d")" + if ! verify_pkg_index "$d" "$pkg"; then + vfail=1 + fi + done + if [ "$vfail" -ne 0 ]; then + die "Verification failed" + fi +fi + +if [ "$VERIFY_ONLY" -eq 1 ]; then + echo "OK: verified indexes under: $SIMPLE_DIR" +else + echo "OK: indexes generated under: $SIMPLE_DIR" +fi