pkget

Binary package manager for CRUX
git clone git://git.emmett1.my/pkget.git
Log | Files | Refs | README

pkgrepo (8155B)


      1 #!/bin/sh
      2 # pkgrepo - binary package repository database generator
      3 # Reads a CRUX-style ports tree and generates a repo DB + checksums
      4 # Usage: pkgrepo [options]
      5 
      6 PORTSDIR="${PORTSDIR:-}"
      7 PKGDIR="${PKGDIR:-/var/pkg/repo}"
      8 PKGEXT="${PKGEXT:-.pkg.tar.gz .pkg.tar.xz .pkg.tar.bz2}"
      9 VERBOSE=0
     10 
     11 usage() {
     12     cat <<EOF
     13 Usage: pkgrepo [options]
     14 
     15 Options:
     16   -p <dir>    Ports tree directory (auto-detected from prt-get if omitted)
     17   -r <dir>    Package repo directory (default: $PKGDIR)
     18   -d <file>   Output DB file (default: \$PKGDIR/repo.db)
     19   -e <exts>   Package extensions, space-separated (default: .pkg.tar.gz .pkg.tar.xz .pkg.tar.bz2)
     20   -v          Verbose output
     21   -h          Show this help
     22 
     23 Environment:
     24   PORTSDIR    Ports tree root(s), space-separated (auto-detected from prt-get if not set)
     25   PKGDIR      Built packages directory
     26   PKGEXT      Package extensions to search, space-separated (default: .pkg.tar.gz .pkg.tar.xz .pkg.tar.bz2)
     27 
     28 
     29 The ports tree should follow standard CRUX layout:
     30   \$PORTSDIR/<name>/Pkgfile
     31   \$PORTSDIR/<name>/pre-install   (optional)
     32   \$PORTSDIR/<name>/post-install  (optional)
     33 
     34 Dependencies are read from the Pkgfile comment:
     35   # Depends on: foo bar baz
     36 EOF
     37     exit 0
     38 }
     39 
     40 log() {
     41     [ "$VERBOSE" -eq 1 ] && printf '%s\n' "$*" >&2
     42 }
     43 
     44 err() {
     45     printf 'pkgrepo: error: %s\n' "$*" >&2
     46 }
     47 
     48 die() {
     49     err "$*"
     50     exit 1
     51 }
     52 
     53 # ---------------------------------------------------------------------------
     54 # _mktemp -- portable temp file creation (GNU + BSD)
     55 # ---------------------------------------------------------------------------
     56 _mktemp() {
     57     mktemp 2>/dev/null || mktemp -t pkgrepo 2>/dev/null || \
     58         die "mktemp failed"
     59 }
     60 
     61 # ---------------------------------------------------------------------------
     62 # _sha256 <file> -- print SHA256 hash, portable across implementations
     63 # ---------------------------------------------------------------------------
     64 _sha256() {
     65     if command -v sha256sum >/dev/null 2>&1; then
     66         sha256sum "$1" | awk '{print $1}'
     67     elif command -v shasum >/dev/null 2>&1; then
     68         shasum -a 256 "$1" | awk '{print $1}'
     69     elif command -v sha256 >/dev/null 2>&1; then
     70         sha256 -q "$1"
     71     else
     72         die "no sha256 tool found (need sha256sum, shasum, or sha256)"
     73     fi
     74 }
     75 
     76 # Parse a Pkgfile and emit fields to stdout
     77 # Output: name, version, release, deps (space-separated), desc
     78 parse_pkgfile() {
     79     _pkgfile="$1"
     80     _name="" _version="" _release="" _deps="" _desc=""
     81 
     82     while IFS= read -r _line; do
     83         case "$_line" in
     84             name=*)       _name="${_line#name=}" ;;
     85             version=*)    _version="${_line#version=}" ;;
     86             release=*)    _release="${_line#release=}" ;;
     87             '# Depends on:'*) _deps="${_line#*# Depends on:}"
     88                               # trim leading whitespace
     89                               _deps="${_deps#"${_deps%%[! ]*}"}"
     90                               # normalize to space-separated (handles commas)
     91                               _deps=$(printf '%s' "$_deps" | tr ',' ' ' | tr -s ' ') ;;
     92             '# Description:'*) _desc="${_line#*# Description:}"
     93                                _desc="${_desc#"${_desc%%[! ]*}"}" ;;
     94         esac
     95     done < "$_pkgfile"
     96 
     97     printf 'name:%s\nversion:%s\nrelease:%s\ndeps:%s\ndesc:%s\n' \
     98         "$_name" "$_version" "$_release" "$_deps" "$_desc"
     99 }
    100 
    101 # Find the built package file for a given name/version/release
    102 # PKGEXT is a space-separated list of extensions to try
    103 find_pkg() {
    104     _n="$1" _v="$2" _r="$3"
    105     for _ext in $PKGEXT; do
    106         # Try exact match first
    107         _path="$PKGDIR/${_n}#${_v}-${_r}${_ext}"
    108         [ -f "$_path" ] && { printf '%s' "$_path"; return 0; }
    109         # Try glob (version/release may differ if pkg was rebuilt)
    110         for _f in "$PKGDIR/${_n}#"*"${_ext}"; do
    111             [ -f "$_f" ] && { printf '%s' "$_f"; return 0; }
    112         done
    113     done
    114     return 1
    115 }
    116 
    117 # Generate repo DB
    118 gen_db() {
    119     _tmpdb=$(_mktemp) || die "failed to create temp file"
    120     _tmpsum=$(_mktemp) || die "failed to create temp file"
    121     _count=0
    122     _missing=0
    123 
    124     for _portsdir in $PORTSDIR; do
    125         [ -d "$_portsdir" ] || continue
    126         for _pkgfile in "$_portsdir"/*/Pkgfile; do
    127             [ -f "$_pkgfile" ] || continue
    128             _portdir="${_pkgfile%/Pkgfile}"
    129             _portname="${_portdir##*/}"
    130 
    131             log "processing: $_portname"
    132 
    133             # Parse fields from Pkgfile
    134             _info=$(parse_pkgfile "$_pkgfile")
    135             _name=$(printf '%s' "$_info" | awk -F: '/^name:/{print $2}')
    136             _version=$(printf '%s' "$_info" | awk -F: '/^version:/{print $2}')
    137             _release=$(printf '%s' "$_info" | awk -F: '/^release:/{print $2}')
    138             _deps=$(printf '%s' "$_info" | awk -F: '/^deps:/{print $2}')
    139             _desc=$(printf '%s' "$_info" | awk -F: '/^desc:/{print $2}')
    140 
    141             # Skip if no name/version (malformed Pkgfile)
    142             [ -z "$_name" ] || [ -z "$_version" ] && {
    143                 err "skipping $_portname: missing name or version"
    144                 continue
    145             }
    146 
    147             # Find built package
    148             _pkgpath=$(find_pkg "$_name" "$_version" "$_release")
    149             if [ $? -ne 0 ] || [ -z "$_pkgpath" ]; then
    150                 log "  WARNING: no built package found for $_name-$_version-$_release"
    151                 _missing=$((_missing + 1))
    152                 continue
    153             fi
    154 
    155             _pkgfile_basename="${_pkgpath##*/}"
    156 
    157             # Checksum the package
    158             _sum=$(_sha256 "$_pkgpath")
    159 
    160             # Embed pre/post install scripts as base64 in the DB stanza
    161             _pre=""
    162             _post=""
    163             if [ -f "$_portdir/pre-install" ]; then
    164                 _pre=$(base64 < "$_portdir/pre-install" | tr -d '\n')
    165             fi
    166             if [ -f "$_portdir/post-install" ]; then
    167                 _post=$(base64 < "$_portdir/post-install" | tr -d '\n')
    168             fi
    169 
    170             # Write stanza to DB
    171             {
    172                 printf 'name:%s\n'    "$_name"
    173                 printf 'version:%s\n' "$_version"
    174                 printf 'release:%s\n' "$_release"
    175                 printf 'file:%s\n'    "$_pkgfile_basename"
    176                 printf 'deps:%s\n'    "$_deps"
    177                 printf 'desc:%s\n'    "$_desc"
    178                 [ -n "$_pre" ]  && printf 'pre-install:b64:%s\n' "$_pre"
    179                 [ -n "$_post" ] && printf 'post-install:b64:%s\n' "$_post"
    180                 printf '\n'
    181             } >> "$_tmpdb"
    182 
    183             # Write checksum
    184             printf '%s  %s\n' "$_sum" "$_pkgfile_basename" >> "$_tmpsum"
    185 
    186             _count=$((_count + 1))
    187             log "  -> $_pkgfile_basename [$_sum]"
    188         done
    189     done
    190 
    191     mv "$_tmpdb"  "$DBFILE"
    192     mv "$_tmpsum" "$SUMFILE"
    193 
    194     printf 'pkgrepo: wrote %d packages to %s\n' "$_count" "$DBFILE"
    195     [ "$_missing" -gt 0 ] && \
    196         printf 'pkgrepo: WARNING: %d ports skipped (no built package)\n' "$_missing"
    197 }
    198 
    199 # --- main ---
    200 
    201 while getopts 'p:r:d:e:vh' _opt; do
    202     case "$_opt" in
    203         p) PORTSDIR="$OPTARG" ;;
    204         r) PKGDIR="$OPTARG" ;;
    205         d) DBFILE="$OPTARG" ;;
    206         e) PKGEXT="$OPTARG" ;;
    207         v) VERBOSE=1 ;;
    208         h) usage ;;
    209         *) usage ;;
    210     esac
    211 done
    212 
    213 # Strip trailing slashes for clean path concatenation
    214 PKGDIR="${PKGDIR%/}"
    215 
    216 # Auto-detect ports directory from prt-get if not set by env or -p flag
    217 if [ -z "$PORTSDIR" ]; then
    218     if command -v prt-get >/dev/null 2>&1 && [ -f /etc/prt-get.conf ]; then
    219         PORTSDIR=$(grep '^prtdir ' /etc/prt-get.conf | awk '{print $2}' | tr '\n' ' ')
    220         PORTSDIR="${PORTSDIR% }"
    221     fi
    222     : "${PORTSDIR:=/usr/ports}"
    223 fi
    224 
    225 # Strip trailing slashes from each ports directory word
    226 _PDIRS=""
    227 for _pd in $PORTSDIR; do
    228     _PDIRS="${_PDIRS} ${_pd%/}"
    229 done
    230 PORTSDIR="${_PDIRS# }"
    231 
    232 # Validate at least one ports directory exists
    233 _ports_ok=0
    234 for _pd in $PORTSDIR; do
    235     [ -d "$_pd" ] && { _ports_ok=1; break; }
    236 done
    237 [ "$_ports_ok" -eq 1 ] || die "no ports directory found (tried: $PORTSDIR)"
    238 
    239 [ -d "$PKGDIR" ]   || die "package repo dir not found: $PKGDIR"
    240 
    241 # Default DBFILE/SUMFILE after PKGDIR is finalized (so -r takes effect)
    242 : "${DBFILE:=$PKGDIR/repo.db}"
    243 : "${SUMFILE:=$PKGDIR/repo.sha256}"
    244 
    245 gen_db