From 259d48d3b11cfa2fb87fabee2165ff474fd54243 Mon Sep 17 00:00:00 2001 From: yafox Date: Wed, 25 Nov 2020 06:23:36 +0000 Subject: [PATCH] initial commit. --- .gitignore | 2 ++ LICENSE | 20 +++++++++++++++++++ README | 44 +++++++++++++++++++++++++++++++++++++++++ USAGE | 6 ++++++ how.sh | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/log.sh | 18 +++++++++++++++++ makefile | 24 ++++++++++++++++++++++ sloc.sh | 12 +++++++++++ 8 files changed, 184 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README create mode 100644 USAGE create mode 100755 how.sh create mode 100755 lib/log.sh create mode 100644 makefile create mode 100755 sloc.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..327f9ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +pkg/ +default/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bd8c239 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright 2020 "yafox" + +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/README b/README new file mode 100644 index 0000000..6ae1606 --- /dev/null +++ b/README @@ -0,0 +1,44 @@ +# how.sh + +takes a package name, a version, and a path. mounts scripts for patching, +configuring, building, and installing the given version of the package at the +given path in read-only mode using either overlayfs or, if no package-specific +scripts were found, a bind mount. + +dependencies: `shsort` and `vercmp`, as well as 'how-defaults' and 'how-pkgs' +repositories. (see makefile.) + +55 SLOC. + +usage: how + +## package structure + +if a package or version is not recognized, how.sh will bind-mount the `default` +directory in read-only mode at the requested path. a package and version is +recognized based on the presence of a subdirectory in the `pkg` directory +matching the package name with a subdirectory whose name translates to a version +equal to or less than version of the package requested, or a subdirectory named +`default`. the structure looks something like this: + + pkg/ + |-- / + | |-- / + ... + +`version` is matched using `vercmp`. if `vercmp` has a format that matches the +package name, then that format is used to compare versions for the package. +otherwise, the default comparison function is used. + +the version directories are sorted using `shsort` and `vercmp` and mounted as an +overlay filesystem with the lower versions being lower in the overlay and higher +versions being higher. `default` package subdirectories are always one layer +above the `default` directory; the `default` directory is always lowest. + +## patches + +multiple patches may be offered in a package. by convention, patches are kept +in the `patches` subdirectory of the package root. the default `patch` command +looks for a single combined patch, likely constructed by concatenating or +otherwise combining offered patches, under the filename `patch` in the package +root. diff --git a/USAGE b/USAGE new file mode 100644 index 0000000..0368660 --- /dev/null +++ b/USAGE @@ -0,0 +1,6 @@ +usage: $(basename $0) [] + +arguments: + : name of the package to examine. + : version of the package to examine. + []: mount path for 'how' scripts. prints "lowerdir" string if omitted. diff --git a/how.sh b/how.sh new file mode 100755 index 0000000..d48b2c8 --- /dev/null +++ b/how.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env sh +set -e + +HOWROOT="${HOWROOT:-$(dirname "$(readlink -f "$0")")}" + +if [ ! -d "$HOWROOT" ]; then + echo "$HOWROOT is not a directory!" >&2 + exit 1 +fi + +{ [ "$1" ] && [ "$2" ]; } \ +|| { eval "echo \"$(cat "$HOWROOT/USAGE")\"" && exit 0; } + +. "$HOWROOT/lib/log.sh" + +pkg="$1" +ver="$2" +tgt="$3" + +if [ "$tgt" ] && [ ! -d "$tgt" ]; then + echo "$tgt isn't a directory!" >&2 + exit 1 +fi + +{ vercmp formats | grep -q "^$pkg"; } \ +&& fmt="$pkg" \ +|| fmt="default" + +[ ! -d "$HOWROOT/pkg/$pkg/default" ] || layers="$HOWROOT/pkg/$pkg/default:" + +if [ -d "$HOWROOT/pkg/$pkg" ]; then + layers="$(ls -d $HOWROOT/pkg/$pkg/*/ \ + | rev | cut -d/ -f2 | rev \ + | shsort "vercmp -f $fmt" \ + | while read dir; do + [ "$dir" ] || continue; + [ "$dir" != "default" ] || continue; + + if [ "$(vercmp -f "$fmt" "$dir <= $ver")" = "yes" ]; then + printf "$HOWROOT/pkg/$pkg/$dir:" + fi + + done)$layers" +fi + +[ "$tgt" ] || { echo "$layers$HOWROOT/default" && exit 0; } + +# mounting an overlayfs fails if only given a single lower directory, so use a +# read-only bind mount if just mounting the default scripts directory. the +# behaviour of a read-only bind mount should be roughly equivalent to an +# overlayfs mounted with a single lower directory and no upper directory. (the +# only difference which comes to mind is that a bind mount does not count +# against overlayfs' nesting limit.) +if [ "$layers" ]; then + mount -t overlay -o lowerdir="$layers$HOWROOT/default" none "$tgt" +else + mount -o ro,bind "$HOWROOT/default" "$tgt" +fi diff --git a/lib/log.sh b/lib/log.sh new file mode 100755 index 0000000..3620f76 --- /dev/null +++ b/lib/log.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +err() { + >&2 echo "[ERR] $1" + exit 1 +} + +warn() { + >&2 echo "[WRN] $1" +} + +log() { + echo "[LOG] $1" +} + +debug() { + [ -z "$DEBUG" ] || echo "[DBG] $1" +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..244606e --- /dev/null +++ b/makefile @@ -0,0 +1,24 @@ +SRCDIR = $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) +PREFIX ?= /usr +HOWROOT ?= $(PREFIX)/share/how +DESTDIR ?= $(PREFIX)/bin +GITBASE ?= $(shell realpath $(shell git config --get remote.origin.url) | rev \ + | cut -d/ -f2- | rev) + +.PHONY: install uninstall lix-os-how + +lix-os-how: lix-os-defaults lix-os-pkgs + +lix-os-defaults: + git clone $(GITBASE)/how-lix-os-defaults default + +lix-os-pkgs: + git clone $(GITBASE)/how-lix-os-pkgs pkg + +install: + ln -sf $(SRCDIR) $(HOWROOT) + ln -sf $(HOWROOT)/how.sh $(DESTDIR)/how + +uninstall: + rm $(DESTDIR)/how + rm $(HOWROOT) diff --git a/sloc.sh b/sloc.sh new file mode 100755 index 0000000..9dd2d8e --- /dev/null +++ b/sloc.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# find all *.sh files not under `pkg` that are not symbolic links, strip all +# trailing whitespace, then all leading whitespace, then all lines starting +# with '#', then all empty lines. then count the remaining lines. + +find . -name "*.sh" ! -path "**/pkg/**" ! -path "**/default/**" ! -type l \ +| xargs sed 's/[[:space:]]*$//g; s/^[[:space:]]*//g; s/^#.*$//g; /^$/d' \ +| wc -l - \ +| cut -d' ' -f1 + +# note that this script's ELOC is also included in the count.