diff --git a/gen-repo-metadata b/gen-repo-metadata new file mode 100755 index 0000000..1fbab3f --- /dev/null +++ b/gen-repo-metadata @@ -0,0 +1,545 @@ +#!/bin/bash +# shellcheck disable=SC2015,SC2059 +VERSION="0.3.2" +# Copyright (c) 2005-2022: +# Darren 'Tadgy' Austin +# Licensed under the terms of the GNU General Public License version 3. + +# Configuration. +REPO_ROOT="${REPO_ROOT:-/data/slackware/repo}" +REPO_PACKAGE_DIRS=( "$REPO_ROOT"/slackware* ) +GPG_KEY="${GPG_KEY:-Darren 'Tadgy' Austin }" + +# Defaults +FORCE=0 +NO_CL=0 +NO_MD5=0 +NO_SIGN=0 +LOCKFILE=".${0##*/}.lock" + +# Timestamp to be used in ChangeLog.txt and a few other files. +NOW="$(date --utc)" + +# Extra shell features. +shopt -s extglob globstar nullglob +set -o pipefail + +die() { + # $* = Error message output before exiting. + printf "\\033[1;31;40m%s\\033[0;39m\\n" "${*:-abort}." >&2 + exit 1 +} + +display_version() { + # |........1.........2.........3.........4.........5.........6.........7.........8 + cat <<-EOF + ${0##*/} v$VERSION. + Copyright (c) 2005-2022 Darren 'Tadgy' Austin . + Licensed under the terms of the GNU GPL v3 . + This program is free software; you can modify or redistribute it in accordence + with the GNU GPL. However, it comes with ABSOLUTELY NO WARRANTY; not even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + EOF +} + +display_help() { + # |........1.........2.........3.........4.........5.........6.........7.........8 + cat <<-EOF + Usage: ${0##*/} [options] [directory] [...] + Generate slapt-get compatible meta-files for a Slackware package repository. + + Options (all of which are optional): + -b, --blurb When generating the ChangeLog.txt, prompt for the blurb + to appear above the individual package log entries, and + for every ChangeLog.txt entry. + -f, --force Force rebuild of metdata for all packages. + -h, --help This help text. + -nc, --no-changelog No ChangeLog.txt updates. + -nm, --no-md5 No .md5sums checksum cache generation for packages. + Without checksum cache files, creation of the repository + CHECKSUMS.md5 file will md5sum every file every time. + -ns, --no-sign No .asc GPG signature generation for packages, or + generation of the final CHECKSUMS.md5.asc file. + -v, --version Display the version and copyright information. + -- Cease option processing and begin argument parsing. + Option processing ceases with the first non-option argument or --. + + Arguments (all of which are optional): + [directory] [...] Only process the (space delimited) list of directories + given on the command line. + EOF +} + +gen_changelog() { + local CUR_X CUR_Y ENTRY JUNK LINES ORIG_X ORIG_Y TEXT + + { [[ -s ".ChangeLog.stub" ]] && { + printf "%s\\n" "$NOW" >&20 + # Read and add header blurb. + (( BLURB == 1 )) && { + # Do some terminal trickery to recover the space used. + printf "\\033[6n" + IFS='[;' read -r -s -d R JUNK ORIG_Y ORIG_X + printf "\\033[1;31;40m%s:\\033[0;39m\\n" "Enter the (optional) ChangeLog.txt header blurb - finish with Ctl+D" + IFS= read -r -d$'\004' TEXT + LINES="$(wc -l <<<"$TEXT")" + # More terminal trickery. + printf "\\033[6n" + IFS='[;' read -r -s -d R JUNK CUR_Y CUR_X + printf "\\033[$(( CUR_Y - LINES ));${ORIG_X}f\\033[J" + # Output the header to the log. + [[ -n "$TEXT" ]] && printf "%s\\n" "$(fmt -s -u -w 78 <<<"$TEXT")" >&20 + } + # Process each package that is in the stub file. + while read -r -u 21 ENTRY; do + (( BLURB == 1 )) && { + printf "\\033[6n" + # shellcheck disable=SC2034 + IFS='[;' read -r -s -d R JUNK ORIG_Y ORIG_X + printf "\\033[1;33;40m%s: %s\\n" "ChangeLog.txt entry" "$ENTRY" + printf "\\033[1;31;40m%s:\\n" "Enter the (optional) blurb for the above ChangeLog.txt entry - finish with Ctl+D" + printf "%s\\033[0;39m\\n" "(Text will be re-formatted, wraped to 78 chars and indented automatically)" + IFS= read -r -d$'\004' TEXT + LINES="$(( $(wc -l <<<"$TEXT") + 2 ))" + printf "\\033[6n" + # shellcheck disable=SC2034 + IFS='[;' read -r -s -d R JUNK CUR_Y CUR_X + printf "\\033[$(( CUR_Y - LINES ));${ORIG_X}f\\033[J" + } + # Output to the log. + printf "%s\\n" "$ENTRY" >&20 + [[ -n "$TEXT" ]] && printf "%s\\n" "$(fmt -s -u -w 76 <<<"$TEXT" | sed -re 's/^/ /g')" >&20 + done 21<.ChangeLog.stub + # If there's a previous changelog, append it to the end of the new one. + [[ -e ChangeLog.txt ]] && printf "+--------------------------+\\n" >&20 + } + [[ -e ChangeLog.txt ]] && cat ChangeLog.txt >&20 + } 20>.ChangeLog.staged + rm -f .ChangeLog.stub + return 0 +} + +gen_checksums() { + local FILE + + # shellcheck disable=SC2094 + { cat <<-EOF + These are the MD5 message digests for the files in this directory. + If you want to test your files, use 'md5sum' and compare the values to + the ones listed here. + + To test all these files, use this command: + + tail +13 CHECKSUMS.md5 | md5sum -c --quiet - | less + + 'md5sum' can be found in the GNU coreutils package on ftp.gnu.org in + /pub/gnu, or at any GNU mirror site. + + MD5 message digest Filename + EOF + + if (( NO_MD5 == 0 )); then + # Create from the .md5sums files if possible, otherwise sum individually. + for FILE in **/!(*.asc|*.manifest|*.md5sums|*-*-*-*.txt|CHECKSUMS.md5); do + if [[ "$FILE" == *.t?z ]]; then + if [[ -e "${FILE%.t?z}.md5sums" ]]; then + # Just cat the .md5sums file. + cat "${FILE%.t?z}.md5sums" + else + # No .md5sums file - sum the relevant files. + if [[ -e "$FILE.asc" ]]; then + md5sum "$FILE" "$FILE.asc" "${FILE%.t?z}.txt" 2>/dev/null || return 1 + else + md5sum "$FILE" "${FILE%.t?z}.txt" 2>/dev/null || return 1 + fi + fi + else + # md5sum the file. + [[ -f "$FILE" ]] && { md5sum "$FILE" || return 1; } + fi + done + else + # Ignore the .md5sums files and sum everything. + find . \( ! -type f -o -path '*/.*' -o -name '*.manifest' -o -name '*.md5sums' -a -prune \) -o -printf "%P\\n" | sort | xargs md5sum || return 1 + fi + } >CHECKSUMS.md5 + return 0 +} + +gen_filelist() { + { printf "%s\\n\\n" "$NOW" + printf "%s\\n" "Here is the file list for this directory. If you are using a" + printf "%s\\n" "mirror site and find missing or extra files in the disk" + printf "%s\\n" "subdirectories, please have the archive administrator refresh" + printf "%s\\n\\n" "the mirror." + + # Trying to use the output of ls is always a bad idea, but there's little choice here.... + find . \( -path '*/.*' -o -name '*.manifest' -o -name '*.md5sums' -a -prune \) -o -print | sort | xargs ls -1bld --time-style=long-iso | \ + sed -re 's/([^[:space:]]+[[:space:]]+[[:digit:]]+[[:space:]]+)[^[:space:]]+([[:space:]]+)[^[:space:]]+(.*)/\1root\2root\3/g' || return 1 + } >FILELIST.TXT + return 0 +} + +gen_manifest() { + local FILE + + # Create from the .manifest cache files if possible, otherwise create inline. + { for FILE in **/*-*-*-*.t?z; do + printf "++========================================\\n||\\n" + printf "|| Package: ./%s\\n" "$FILE" + printf "||\\n++========================================\\n" + if [[ -e "${FILE%.t?z}.manifest" ]]; then + # Include the .manifest file. + cat "${FILE%.t?z}.manifest" 2>/dev/null || return 1 + else + # No .manifest file. + tar -vvtf "$FILE" 2>/dev/null || return 1 + fi + printf "\\n\\n" + done + } | bzip2 -c9 >MANIFEST.bz2 2>/dev/null || return 1 + return 0 +} + +gen_packages() { + { printf "%s; %s\\n\\n" "PACKAGES.TXT" "$NOW" + printf "%s\\n%s\\n\\n" "This file provides details on the Slackware packages found" "in this directory." + # shellcheck disable=SC2046 + printf "%s: %s MB\\n" "Total size of all packages (compressed)" "$(numfmt --from=iec --to-unit=1048576 "$(( \ + ($(awk -F' ' -e '/^PACKAGE SIZE \(compressed\)/ { print $4 " + "}' $(find . -type f -name '*-*-*-*.txt'; printf "/dev/null"))0) * 1024 ))")" + # shellcheck disable=SC2046 + printf "%s: %s MB\\n\\n\\n" "Total size of all packages (uncompressed)" "$(numfmt --from=iec --to-unit=1048576 "$(( \ + ($(awk -F' ' -e '/^PACKAGE SIZE \(uncompressed\)/ { print $4 " + "}' $(find . -type f -name '*-*-*-*.txt'; printf "/dev/null"))0) * 1024 ))")" + for TXT in **/*-*-*-*.txt; do + cat "$TXT" || return 1 + printf "\\n" + done + } >PACKAGES.TXT + return 0 +} + +gen_pkg_changelog() { + # $1 = Package file to process [required]. + local CACHE=() CACHED_ARCH CACHED_BUILD CACHED_DIR CACHED_EXT CACHED_TAG CACHED_VER + local I J PKG_ARCH PKG_BUILD PKG_DIR PKG_EXT PKG_NAME PKG_TAG PKG_VER TMP + + [[ -z "$1" ]] || [[ ! -e "$1" ]] && die "abort: ${FUNCNAME[0]} ($LINENO)" + + # Get the new package details. + PKG_DIR="${1%/*}"; [[ "$PKG_DIR" == "$1" ]] && PKG_DIR="" + PKG_NAME="$(printf "$1" | rev | cut -d- -f4- | cut -d/ -f1 | rev)" + PKG_VER="$(printf "$1" | rev | cut -d- -f3 | rev)" + PKG_ARCH="$(printf "$1" | rev | cut -d- -f2 | rev)" + TMP="$(printf "$1" | rev | cut -d- -f1 | rev)" + PKG_BUILD="${TMP%%[^[:digit:]]*}" + TMP="${TMP/#+([[:digit:]])}" + PKG_TAG="${TMP%.t?z}" + PKG_EXT="${TMP##*.}" + unset TMP + + # Read the cache file. + [[ -e ".versions.cache" ]] && { mapfile -t CACHE <.versions.cache || die "Failed to read .versions.cache"; } + + # Find the package details in the array. + for ((I = 0; I < ${#CACHE[*]}; I++)); do + IFS=';' read -r CACHED_DIR CACHED_NAME CACHED_VER CACHED_ARCH CACHED_BUILD CACHED_TAG CACHED_EXT <<<"${CACHE[I]}" + [[ "$CACHED_NAME" == "$PKG_NAME" ]] && break + done + + { # If there's no CACHED_VER set, then there's either no cache or the package isn't in it. + # Either way, just mark the package as added. + if [[ -z "$CACHED_VER" ]]; then + printf "%s: %s.\\n" "$1" "Added" + elif [[ "$PKG_DIR" != "$CACHED_DIR" ]] || [[ "$PKG_EXT" != "$CACHED_EXT" ]]; then + # Package has changed directory or extension - mark new one as added; the other as removed. + printf "%s: %s.\\n" "$1" "Added" + printf "%s: %s.\\n" "${CACHED_DIR:+$CACHED_DIR/}$PKG_NAME-$CACHED_VER-$CACHED_ARCH-$CACHED_BUILD$CACHED_TAG.$CACHED_EXT" "Removed" + elif [[ "$PKG_VER" != "$CACHED_VER" ]]; then + # Versions are different, no need to do more than mark as upgraded. + printf "%s: %s.\\n" "$1" "Upgraded" + else + # Versions are the same, check the build details. + if [[ "$PKG_TAG" != "$CACHED_TAG" ]]; then + # Different build tag, no need to test the build number. + # Mark the new package as added; the old one as removed. + printf "%s: %s.\\n" "$1" "Added" + printf "%s: %s.\\n" "${CACHED_DIR:+$CACHED_DIR/}$PKG_NAME-$CACHED_VER-$CACHED_ARCH-$CACHED_BUILD$CACHED_TAG.$CACHED_EXT" "Removed" + elif (( PKG_BUILD != CACHED_BUILD )); then + # If the build numbers differ, just mark a rebuild. + printf "%s: %s.\\n" "$1" "Rebuilt" + fi + fi + } | sort >>.ChangeLog.stub + + # There may still be old entries for the package in the cache. + for ((J = I; J < ${#CACHE[*]}; J++)); do + [[ "$(printf "${CACHE[J]}" | cut -d: -f2)" == "$PKG_NAME" ]] && unset 'CACHE[J]' + done + + # Remove the upgraded/rebuilt package from the cache. + [[ -n "${CACHE[I]}" ]] && unset 'CACHE[I]' + + # Finally, add the new package to the cache. + CACHE+="$PKG_DIR;$PKG_NAME;$PKG_VER;$PKG_ARCH;$PKG_BUILD;$PKG_TAG;$PKG_EXT" + + # Write the new cache file. + printf "%s\\n" "${CACHE[@]}" | sort -r >.versions.cache +} + +gen_pkg_md5sums() { + # $1 = Package file to process [required]. + local TXT="${1%.t?z}.txt" + local MD5="${1%.t?z}.md5sums" + + [[ -z "$1" ]] || [[ ! -e "$1" ]] && die "abort: ${FUNCNAME[0]} ($LINENO)" + if [[ -e "$1.asc" ]]; then + md5sum "$1" "$1.asc" "$TXT" >"$MD5" || return 1 + else + md5sum "$1" "$TXT" >"$MD5" || return 1 + fi + return 0 +} + +gen_pkg_txt() { + # $1 = Package file to process [required]. + local TXT="${1%.t?z}.txt" + + [[ -z "$1" ]] || [[ ! -e "$1" ]] && die "abort: ${FUNCNAME[0]} ($LINENO)" + { printf "%s: %s\\n" "PACKAGE NAME" "${1##*/}" + printf "%s: %s\\n" "PACKAGE LOCATION" "./${1%/*}" + printf "%s: %s\\n" "PACKAGE SIZE (compressed)" "$(du -k "$1" | cut -d $'\t' -f 1) K" + printf "%s: %s\\n" "PACKAGE SIZE (uncompressed)" "$(numfmt --to-unit=1024 "$(tar -xOf "$1" | wc -c)") K" + printf "%s: %s\\n" "PACKAGE REQUIRED" "$(tar --occurrence=1 -xOf "$1" install/slack-required 2>/dev/null | tr $'\n' ',' | sed -e 's/,$//')" + printf "%s: %s\\n" "PACKAGE CONFLICTS" "$(tar --occurrence=1 -xOf "$1" install/slack-conflicts 2>/dev/null | tr $'\n' ',' | sed -e 's/,$//')" + printf "%s: %s\\n" "PACKAGE SUGGESTS" "$(tar --occurrence=1 -xOf "$1" install/slack-suggests 2>/dev/null | tr $'\n' ' ' | sed -e 's/ $//')" + printf "%s:\\n" "PACKAGE DESCRIPTION" + tar --occurrence=1 -xOf "$1" install/slack-desc 2>/dev/null + } >"$TXT" || return 1 + return 0 +} + +runtime() { + # $1 = Number of seconds to convert to readable text [required]. + + [[ -z "$1" ]] && die "abort: ${FUNCNAME[0]} ($LINENO)" + local D=$(( $1 / 86400 )) + local H=$(( ($1 - (D * 86400)) / 3600 )) + local M=$(( ($1 - (D * 86400) - (H * 3600)) / 60 )) + local S=$(( $1 - (D * 86400) - (H * 3600) - (M * 60) )) + + if (( D > 0 )); then + printf "%s, %s %s %s" "${D}d" "${H}h" "${M}m" "${S}s" + else + printf "%s, %s %s" "${H}h" "${M}m" "${S}s" + fi + return 0 +} + +sign_file() { + # $1 = The file to sign [required]. + + [[ -z "$1" ]] || [[ ! -e "$1" ]] && die "abort: ${FUNCNAME[0]} ($LINENO)" + gpg2 --sign --detach-sign --armor --batch --quiet --local-user "$GPG_KEY" "$1" || return 1 + return 0 +} + + +# Parse command line options. +while [[ -n "$1" ]]; do + case "$1" in + -b|-blub|--blurb) + BLURB=1 + shift + ;; + -f|-force|--force) + FORCE=1 + shift + ;; + -h|-help|--help) + display_help + exit 0 + ;; + -nc|-no-changelog|--no-changelog) + NO_CL=1 + shift + ;; + -nm|-no-md5|--no-md5) + NO_MD5=1 + shift + ;; + -ns|-no-sign|--no-sign) + NO_SIGN=1 + shift + ;; + -v|-version|--version) + display_version + exit 0 + ;; + --) + shift + break + ;; + -*) + printf "%s: %s: %s\\n" "${0##*/}" "invalid option" "$1" >&2 + printf "Try: %s %s\\n" "${0##*/}" "--help" >&2 + exit 1 + ;; + *) + break + ;; + esac +done + +# Operate from the top level of the repository. +cd "$REPO_ROOT" || die "Failed to change directory to repo root" + +# Any remaining command line arguments are the directories to process. +[[ -n "$*" ]] && REPO_PACKAGE_DIRS=( "$@" ) || REPO_PACKAGE_DIRS=( slackware* ) + +# Sanity check of REPO_PACKAGE_DIRS. +for ((I = 0; I < ${#REPO_PACKAGE_DIRS[*]}; I++)); do + [[ ! -e "${REPO_PACKAGE_DIRS[I]}" ]] && die "no such package directory: ${REPO_PACKAGE_DIRS[I]}" +done + +# Create lockfile. +{ exec {FD}>"$LOCKFILE" && flock -E 10 -e -w 0.5 "$FD"; } || { + if (( $? == 10 )); then + die "Failed to obtain lockfile - another instance is running" + else + die "flock usage error" + fi +} +trap 'exec {FD}<&-; rm -f "$LOCKFILE"' EXIT INT TERM + +# Check gpg-agent and unlock key if required. +(( NO_SIGN == 0 )) && { + gpg2 -K "$GPG_KEY" >/dev/null 2>&1 + (( $? == 2 )) && die "no key available for ID: $GPG_KEY" + printf "test" | gpg2 --sign -o /dev/null --pinentry-mode error --local-user "$GPG_KEY" >/dev/null 2>&1 + (( $? == 2 )) && { + printf "\\033[1;31;40m%s\\033[0;39m\\n" "GPG signing key is locked - passphrase required." + while :; do + printf "test" | gpg2 --sign -o /dev/null --local-user "$GPG_KEY" 2>/dev/null + ERR=$? + (( ERR == 130 )) && die "caught interrupt - aborting" + (( ERR == 2 )) && { + printf "\\033[1;31;40m%s\\033[0;39m" "Incorrect passphrase. Retry? (Y/n): " + read -r -N 1 + printf "\\n" + [[ "$REPLY" =~ [Nn] ]] && die "cannot continue without unlocking GPG signing key" + continue + } + (( ERR > 0 )) && die "unknown error -aborting" + break + done + } +} + +# Process the package dirs. +for ((I = 0; I < ${#REPO_PACKAGE_DIRS[*]}; I++)); do + cd "${REPO_PACKAGE_DIRS[I]}" >/dev/null 2>&1 || die "abort: $LINENO" + printf "\\033[1;32;40m%s '%s':\\033[0;39m\\n" "Processing repository directory" "${REPO_PACKAGE_DIRS[I]}" + SECONDS=0 + + # Sanity check: make sure there is not more than one package of each name in the repository. + PKGS=() + for FILE in **/*-*-*-*.t?z; do + PKG_NAME="$(printf "$FILE" | rev | cut -d- -f4- | cut -d/ -f1 | rev)" + PKG_ARCH="${PKG_ARCH:-$(printf "$FILE" | rev | cut -d- -f2 | rev)}" + if printf "%s " "${PKGS[@]}" | grep -E "\<$PKG_NAME\>" >/dev/null 2>&1; then + die "Repository corrupt: more than one package of the same name in repository ($PKG_NAME)" + elif [[ "$PKG_ARCH" != "$(printf "$FILE" | rev | cut -d- -f2 | rev)" ]]; then + die "Repository corrupt: more than one package architecture in repository" + else + PKGS+=("$PKG_NAME") + fi + done + unset FILE PKG_ARCH PKG_NAME PKGS + + # Process each package in repository. + for FILE in **/*-*-*-*.t?z; do + printf "\\033[1;33;40m %s: " "$FILE" + if (( FORCE == 1 )) || [[ "$FILE" -nt "$FILE.asc" ]] || [[ "$FILE" -nt "${FILE%.t?z}.txt" ]] || [[ "$FILE" -nt "${FILE%.t?z}.md5sums" ]] || \ + [[ "$FILE" -nt "${FILE%.t?z}.manifest" ]] || ! grep -E "\<$(gpg2 --list-packets "$FILE.asc" | awk -e '/:signature packet:/ { print $6 }')\>" \ + < <(gpg2 --list-keys --with-colons "$GPG_KEY" | awk -F: -e '/sub/ { if ($11 == "") print $5 }') >/dev/null 2>&1; then + rm -f "$FILE.asc" "${FILE%.t?z}.txt" "${FILE%.t?z}.md5sums" + + # GPG signature. + (( NO_SIGN == 0 )) && { sign_file "$FILE" && printf "%s " "signed" || die "signing failed"; } + + # .txt file metadata. + gen_pkg_txt "$FILE" && printf "%s " "metadata generated" || die "metadata generation failed" + + # md5sums cache. + (( NO_MD5 == 0 )) && { gen_pkg_md5sums "$FILE" && printf "%s " "md5sums calculated" || die "md5sums calculation failed"; } + + # Manifest cache. + (( VERBOSE == 1 )) && printf " %s " "manifest" + tar -vvtf "$FILE" >"${FILE%.t?z}.manifest" && printf "%s " "manifest extracted" || die "manifest extraction failed" + + # ChangeLog.txt entry. + (( NO_CL == 0 )) && { gen_pkg_changelog "$FILE" && printf "%s" "changelog created" || die "changelog creation failed"; } + + # Force update of top-level repository files. + FLAG_UPDATES=1 + else + printf "\\033[1;32;40m%s\\033[0;39m\\n" "up to date" + FLAG_UPDATES=0 + fi + done + + # Formatting. + printf "\\033[0;39m\\n" + + # Only update the top level files if there was a change in the repository or files don't exist. + (( FLAG_UPDATES == 1 )) || [[ ! -e "GPG-KEY" ]] || [[ ! -e "PACKAGES.TXT" ]] || [[ ! -e "FILELIST.TXT" ]] || [[ ! -e "MANIFEST.bz2" ]] || \ + [[ ! -e "CHECKSUMS.md5" ]] && { + printf "\\033[1;33;40m %s\\n" "Generating:" + + # Add the GPG signing key if required. + (( FORCE == 1 )) || [[ ! -e GPG-KEY ]] || [[ "$(gpg2 --with-colons /dev/null | awk -F: -e '/^fpr/ { print $10; exit }')" != \ + "$(gpg2 --with-colons --list-keys "$GPG_KEY" 2>/dev/null | awk -F: -e '/^fpr/ { print $10; exit }')" ]] && (( NO_SIGN == 0 )) && { + printf " %s\\n" "GPG-KEY" + gpg2 --list-keys "$GPG_KEY" >GPG-KEY && gpg2 --export --armor "$GPG_KEY" >>GPG-KEY || die "Failed to export GPG key" + } + + # Pull all the ChangeLog parts together. + (( NO_CL == 0 )) && [[ -e ".ChangeLog.stub" ]] && { + printf " %s\\n" "ChangeLog.txt" + gen_changelog && mv .ChangeLog.staged ChangeLog.txt 2>/dev/null || die "Failed to generate ChangeLog.txt" + } + + # Create the top-level PACKAGES.TXT. + printf "\\033[1;33;40m %s\\n" "PACKAGES.TXT" + gen_packages || die "Failed to generate PACKAGES.TXT" + + # Generate MANIFEST.bz2. + printf " %s\\n" "MANIFEST.bz2" + gen_manifest || die "Failed to generate MANIFEST.bz2" + + # Create stub files to be included in FILELIST.TXT. + : >CHECKSUMS.md5 + (( NO_SIGN == 0 )) && : >CHECKSUMS.md5.asc + + # Crate FILELIST.TXT. + printf " %s\\n" "FILELIST.TXT" + gen_filelist || die "Failed to generate FILELIST.TXT" + + # Generate repository CHECKSUMS.md5. + printf " %s\\n" "CHECKSUMS.md5" + gen_checksums || die "Failed to generate CHECKSUMS.md5" + + (( NO_SIGN == 0 )) && printf " %s:\\n" "Signing" + + # Sign the checksums. + (( NO_SIGN == 0 )) && printf " %s\\n" "CHECKSUMS.md5" + (( NO_SIGN == 0 )) && { rm -f CHECKSUMS.md5.asc; sign_file "CHECKSUMS.md5" || die "Failed to sign CHECKSUMS.md5"; } + } + + # Sanity check. + [[ ! -e "CHECKSUMS.md5" ]] || [[ ! -e "FILELIST.TXT" ]] || [[ ! -e "PACKAGES.TXT" ]] && \ + printf "\\033[1;31;40m%s\\033[0;39m\\n" "Warning: files missing from repository - not Slackware compliant." + + printf "\\033[1;32;40m%s: %s\\033[0;39m\\n" "Repository updated in" "$(runtime "$SECONDS")." + cd - >/dev/null 2>&1 || die "abort: $LINENO" +done