kiss-bin

kiss extension for binary package management
git clone git://git.carbslinux.org/kiss-bin
Log | Files | Refs | README | LICENSE

commit 43eeb65fbfebce02a99bfeec7759be313c36974e
Author: Cem Keylan <cem@ckyln.com>
Date:   Tue, 26 May 2020 19:34:44 +0300

kiss-bin: initial commit

Diffstat:
ALICENSE | 21+++++++++++++++++++++
AMakefile | 16++++++++++++++++
AREADME.md | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Akiss-bin | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Akiss-bin-manifest | 43+++++++++++++++++++++++++++++++++++++++++++
5 files changed, 337 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Cem Keylan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,16 @@ +PREFIX = /usr/local +BINDIR = ${PREFIX}/bin + +all: + @echo use 'make install' + +install: + mkdir -p ${DESTDIR}${BINDIR} + cp -f kiss-bin ${DESTDIR}${BINDIR}/kiss-bin + cp -f kiss-bin-manifest ${DESTDIR}${BINDIR}/kiss-bin-manifest + chmod 755 ${DESTDIR}${BINDIR}/kiss-bin \ + ${DESTDIR}${BINDIR}/kiss-bin-manifest + +uninstall: + rm -f ${DESTDIR}${BINDIR}/kiss-bin \ + ${DESTDIR}${BINDIR}/kiss-bin-manifest diff --git a/README.md b/README.md @@ -0,0 +1,69 @@ +kiss-bin +-------- + +A KISS extension for dealing with binary package repositories. + +I was initially planning on implementing this inside my fork of +the package manager itself, but I didn't because it would wildly +complicate the package manager. + +There are some caveats that I hope to fix as I work more on this +extension + + +Quick Start +----------- + +You can quickly setup a distribution by doing the following, also +see the Caveats section at the end of the file for the rationale +behind the linking. This sets a distribution to the current directory. + + for bin in ~/.cache/kiss/bin/*; do + binout=${bin##*/} binname=${bin%\#*} binout=${binname}.${binout#*\#} + ln -sf "$bin" "$binout" + done + + kiss-bin-manifest | sort -rV > manifest + + httpd -f -p 8181 + + +Following the previous step, you can do a quick setup in a client +by doing the following. The `$KISS_BIN` variable is explained below. + + export KISS_BIN=http://192.168.x.x:8181:personal + + # This will fetch the manifest from the server + kiss-bin fetch + + +Using kiss-bin +-------------- + +`kiss-bin` can be used to track binary repositories, or downloading +singular packages without tracking any. Repositories are defined through +the `$KISS_BIN` variable. It has a comma seperated value of url and names, +`KISS_BIN=url:name,url2:name2,url3:name3`. + +- `fetch` -- Sync the manifest from repositories +- `get` -- Install a package from the given url +- `install` -- Install a package from the repository +- `search` -- Search for a package. +- `update` -- Update binary packages + + +Usign kiss-bin-manifest +----------------------- + +`kiss-bin-manifest` generates a manifest from the packages on the current +directory. If file names are given, it will generate a manifest for those +files, which can be used for incremental updates. + + +Caveats and Future Plans +------------------------ + +- In http file names with `#` are problematic. Some protocols + expect '%23' and some don't. That's why the `#` must be + replaced with `.` +- I plan on implementing GnuPG support. diff --git a/kiss-bin b/kiss-bin @@ -0,0 +1,188 @@ +#!/bin/sh -ef +# Install binary packages from a distributed repository +# shellcheck source=/dev/null + +# Load KISS as a library, this way we can use internal +# functions variables without complicating the main source +# code. I made sure that I do not use anything specific to +# my own fork of KISS. +# +# We remove the last line calling the 'main "$@"' function. +# We are going to do it ourselves after we define functions. +sed '$d' "$(command -v kiss)" > .kisslib +. ./.kisslib +rm -f .kisslib + +pkg_sync() { + # Word splitting is intentional here. + # shellcheck disable=2086 + { IFS=,; set -- $KISS_BIN; unset IFS; } + + for repo do + log "Syncing ${repo##*:}" + curl "${repo%:*}/manifest" -fLo "$repo_dir/${repo##*:}" + done +} + +pkg_download() { + # Extract the basename and the package name + # from the source. We then get the path we + # will be downloading to. + pkg=${1##*/} pkgname=${pkg%%.*} + pkgfile="${2:-$PWD}/${pkgname}#${pkg#*.}" + + # If we have the package downloaded on the + # cache directory, we can skip the download + [ -f "$pkgfile" ] && { + log "$pkgname" "Found binary package '${pkgfile##*/}'" + return 0 + } + + log "$pkgname" "Downloading from $src" + curl "$src" -fLo "$pkgfile" +} + +pkg_find() { + query=$1 match=$2 IFS=,; set -- + + for path in $KISS_BIN; do + url=${path%:*} + path=$repo_dir/${path##*:} + set +f + unset IFS + + while read -r pkg file sha; do + # We want globbing here, it is safe and intentional. + # shellcheck disable=2254 + case "$pkg" in $query) set -f -- "$@" "${path##*/}:$pkg:$sha:$url/$file"; esac + done < "$path" + done + + [ "$1" ] || die "Package '$query' not in any repository" + + [ "$match" ] && printf '%s\n' "$@" || printf '%s\n' "$1" +} + + +pkg_install() { + # Prompt the user if there are multiple packages or there + # is an update. + [ "$update" ] || [ $# -gt 1 ] && + prompt "Download and install packages? [$*]" + + for pkg; do + IFS=: read -r _ _ hash url <<-EOF + $(pkg_find "$pkg") +EOF + # 'bin_dir' is specified in kiss, so we can ignore this. + # shellcheck disable=2154 + pkg_download "$url" "$bin_dir" + + # We use the sh256 function from KISS to validate + # the checksums + log "$pkg" "Verifying digest" + [ "$hash $pkgfile" = "$(sh256 "$pkgfile")" ] || + die "$pkg" "Checksum mismatch" + + files="$pkgfile $files" + done + + # Word splitting is intentional. + # shellcheck disable=2086 + kiss i $files +} + +pkg_updates() { + # This handles binary packages similar to the pkg_updates + # function on kiss + pkg_sync + + log "Checking for new package versions" + + set +f + + # shellcheck disable=2154 + for pkg in "$sys_db/"*; do + pkg_name=${pkg##*/} + + # Read version and release information from the installed + # packages and from the manifest files. + read -r db_ver db_rel < "$pkg/version" + + # Read the output of pkg_find and strip unneeded components + man_ver="$(pkg_find "$pkg_name")" + man_ver=${man_ver##*/} man_ver=${man_ver#*.} man_ver=${man_ver%%.tar*} + + [ "$db_ver-$db_rel" = "$man_ver" ] || { + printf '%s\n' "$pkg_name $db_ver-$db_rel ==> $man_ver" + outdated="$outdated$pkg_name " + } + done + + set -f + + [ "$outdated" ] || { + log "Everything is up to date" + return + } + + update=1 + # Word splitting is intentional. + # shellcheck disable=2086 + pkg_install $outdated + + log "Updated all packages" +} + +args() { + action=$1 + [ "$action" ] && shift + + # These actions require an argument + case "$action" in g|get|i|install|s|search) + [ "$1" ] || die "'kiss-bin $action' requires an argument" + esac + + case "$action" in i|install|s|search|f|fetch|u|update) + # Search for the KISS_BIN environment value and exit if + # it cannot be found. + [ "$KISS_BIN" ] || die "\$KISS_BIN needs to be set." + + # Let's create the cache directory for binary repositories. + mkdir -p "${repo_dir:=$cac_dir/bin-repos}" + ;; + esac + + case "$action" in + f|fetch) pkg_sync ;; + i|install) pkg_install "$@" ;; + s|search) pkg_find "$1" all ;; + u|updates) pkg_updates ;; + g|get) + for src do + # 'bin_dir' is specified in kiss, so this + # can be safely ignored. + # shellcheck disable=2154 + pkg_download "$src" "$bin_dir" + files="$pkgfile $files" + done + + # We want word-splitting here. + # shellcheck disable=2086 + kiss i $files + ;; + ''|--help|-h|help|h) + log "kiss-bin [g|i|s|u] [pkg]..." + log 'fetch Sync repository manifests' + log 'get Install a package from a url' + log 'install Install a package' + log 'search Search for a package' + log 'update Update binary packages' + ;; + *) + die "Unknown action '$action'" + esac +} + +# Run the main function of KISS +main "$@" diff --git a/kiss-bin-manifest b/kiss-bin-manifest @@ -0,0 +1,43 @@ +#!/bin/sh -ef +# Output a manifest for a binary repository +case "$1" in --help|-h) printf '%s\n' "usage: ${0##*/} [file]..." >&2 ; exit 0 ; esac + +sh256() { + # We will not be sourcing kiss just for this + # function that we'll use. So we are defining + # something similar here. + hash=$(sha256sum "$1" || + sha256 -r "$1" || + openssl dgst -sha256 -r "$1" || + shasum -a 256 "$1" || + digest -a sha256 "$1") 2>/dev/null +} + +# If no arguments are given, output every file on +# the current directory. +[ "$1" ] || { set +f; set -f -- ./*.*-*.tar* ;} + +# We intentionally do no sorting here. If there are +# only single versions for every package, it is quite +# unnecessary. If you are doing an incremental update +# to a manifest, it will be useless as you would be +# re-sorting it after you are finished. +# +# You could do 'sort -rV' to the manifest, but it is +# not POSIX. You could also do +# +# sort -rnt. -k1,1 -k2,2 -k3,3 +# +# which doesn't output a 'nice' looking manifest, and +# is a little error-prone, but it will work with POSIX +# implementations. Regardless, I will be suggesting the +# former if you are using busybox or coreutils. +for file; do + file=${file##*/} + [ -f "$file" ] || { + printf '%s\n' "$file must be on the current working directory" >&2 + exit 1 + } + sh256 "$file" + printf '%s %s %s\n' "${file%%.*}" "$file" "${hash%% *}" +done