#!/bin/bash # ============================================================================= # repo-index.sh – buduje indeks repozytorium PaganOS z gotowych .pag # # Dla każdego pakietu w katalogu wyjściowym: # • wylicza SHA256, # • czyta metadata.json z wnętrza .pag, # • generuje podpis detach (GPG), jeśli klucz dostępny, # a na końcu tworzy: # • repo.json – pełny indeks (czytelny dla menedżera pakietów), # • packages.db – lekka baza (name/version/sha256/path), # • repo.db – alias/symlink do repo.json (kompat staranne narzędzia). # # Użycie: # ./repo-index.sh [--out /var/cache/pagbuild/output] [--repo ./repo] \ # [--gpg-key KEYID] [--sign-all] # ============================================================================= set -euo pipefail OUT_DIR="${1:-/var/cache/pagbuild/output}" REPO_DIR="${REPO_DIR:-$PWD/repo}" GPG_KEY="${GPG_KEY:-}" SIGN_ALL=0 while [[ $# -gt 0 ]]; do case "$1" in --out) OUT_DIR="$2"; shift 2 ;; --repo) REPO_DIR="$2"; shift 2 ;; --gpg-key) GPG_KEY="$2"; shift 2 ;; --sign-all) SIGN_ALL=1; shift ;; -h|--help) sed -n '1,20p' "$0"; exit 0 ;; *) echo "Nieznany argument: $1" >&2; exit 2 ;; esac done mkdir -p "$REPO_DIR" # Znajdź python3 (host) PY=python3 command -v "$PY" >/dev/null || { echo "Brak python3" >&2; exit 1; } # Podpis (jeśli zdefiniowano klucz lub --sign-all i klucz istnieje) GPG="$(command -v gpg2 || command -v gpg || true)" sign_file() { # $1 -> plik # Usuń stary podpis ZAWSZE: detach-sign nie nadpisuje istniejącego .asc # ("File exists"), a nadpisany repo.json bez świeżego .asc = BAD SIGNATURE # dla klientów pag. Bez klucza też usuwamy – lepszy czytelny brak podpisu # niż nieaktualny (klienci: fail-closed „nie można pobrać podpisu”). rm -f "${1}.asc" [[ -n "$GPG" && -n "$GPG_KEY" ]] || return 0 "$GPG" --detach-sign --armor --local-user "$GPG_KEY" --batch --no-tty "$1" 2>/dev/null \ && echo " -> ${1}.asc" || echo " (brak podpisu: ${1})" } # Zbierz pakiety mapfile -t PKGS < <(find "$OUT_DIR" -maxdepth 1 -name '*.pag' | sort) if [[ ${#PKGS[@]} -eq 0 ]]; then echo "Brak pakietów w $OUT_DIR" >&2 exit 1 fi echo "Indeksowanie ${#PKGS[@]} pakietów z $OUT_DIR" ENTRIES_JSON=() DB_ROWS=() for f in "${PKGS[@]}"; do name_full=$(basename "$f") # metadata.json wewnątrz .pag (top-level) sha=$(sha256sum "$f" | awk '{print $1}') meta=$("$PY" -c " import sys, tarfile, json, io try: t=tarfile.open('$f','r:xz') b=t.extractfile('metadata.json').read() print(json.dumps(json.loads(b.decode()))) except Exception: print('{}') " 2>/dev/null || echo '{}') # Podstawowe pola (z metadata albo z nazwy) entry=$("$PY" - "$f" "$sha" "$meta" <<'PY' import sys, json, re fn, sha, meta = sys.argv[1], sys.argv[2], sys.argv[3] or '{}' try: m=json.loads(meta) except Exception: m={} base=fn[:-4] if fn.endswith('.pag') else fn name=m.get('name') or base.rsplit('-',2)[0] ver =m.get('version') or base.rsplit('-',2)[1] rel =str(m.get('release', m.get('pkgrel', 1))) # filename = sama nazwa (bez ścieżki) – menedżer pakietów pobiera go z repo fn_base = __import__('os').path.basename(fn) print(json.dumps({ 'file': fn_base, 'sha256': sha, 'name': name, 'version': ver, 'release': rel, 'size': __import__('os').path.getsize(fn), 'description': m.get('description',''), 'license': m.get('license',[]), 'depends': m.get('depends',[]), 'url': m.get('url',''), 'filename': fn_base, # Wirtualne zależności (sekcja `provides:` w PAGBUILD.yaml) MUSZĄ trafić do # repo.json – inaczej klient `pag` nie rozwiąże np. `python3` -> `python` # i odmówi instalacji pakietu, którego zależność jest wirtualna. # pagbuild zapisuje je w metadata.json; dawne wersje tego skryptu je gubiły. 'provides': m.get('provides',[]), 'provides_so': m.get('provides_so',[]), 'requires_so': m.get('requires_so',[]), })) PY ) ENTRY="{\"$entry\"}" # zbuduj obiekt JSON poprawnie ENTRY=$("$PY" -c "import sys,json; print(json.dumps(json.loads('$entry')))") ENTRIES_JSON+=("$entry") row=$("$PY" -c "import sys,json; d=json.loads('$entry'); print(f\"{d['name']}|{d['version']}|{d['release']}|{d['sha256']}|{d['file']}\")") DB_ROWS+=("$row") if [[ $SIGN_ALL -eq 1 ]]; then sign_file "$f" fi echo " + $name_full ($([ -n "$sha" ] && echo "${sha:0:12}" || echo '?')...)" done # repo.json – jeden obiekt JSON z listą { echo '{' echo ' "generated": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",' echo ' "count": '"${#PKGS[@]}"',' echo ' "packages": [' for i in "${!ENTRIES_JSON[@]}"; do sep="," [[ $i -lt $(( ${#ENTRIES_JSON[@]} - 1 )) ]] || sep="" echo " ${ENTRIES_JSON[$i]}$sep" done echo ' ]' echo '}' } > "$REPO_DIR/repo.json" # packages.db – prosta tabela { echo "#name|version|release|sha256|file" printf '%s\n' "${DB_ROWS[@]}" } > "$REPO_DIR/packages.db" # repo.db – alias do repo.json (kompat) ln -sf repo.json "$REPO_DIR/repo.db" # Podpisz cały indeks (jeśli klucz) sign_file "$REPO_DIR/repo.json" # Sumy indeksu sha256sum "$REPO_DIR/repo.json" > "$REPO_DIR/repo.json.sha256" echo "" echo "Repozytorium gotowe w: $REPO_DIR" echo " repo.json ($(wc -c < "$REPO_DIR/repo.json") B)" echo " packages.db ($(wc -l < "$REPO_DIR/packages.db") wierszy)" echo " repo.json.sha256" [[ -n "$GPG_KEY" ]] && echo " Podpisano indeks i pakiety (klucz: $GPG_KEY)"