commit 43eeb65fbfebce02a99bfeec7759be313c36974e
Author: Cem Keylan <cem@ckyln.com>
Date: Tue, 26 May 2020 19:34:44 +0300
kiss-bin: initial commit
Diffstat:
A | LICENSE | | | 21 | +++++++++++++++++++++ |
A | Makefile | | | 16 | ++++++++++++++++ |
A | README.md | | | 69 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | kiss-bin | | | 188 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | kiss-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