From 3f752d42e994bd1d78f86ab5a242b988428f798e Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 00:26:43 +0300 Subject: [PATCH 01/43] kiss-new: Started rewriting kiss. --- kiss | 40 +++++++++++++++++------- kiss-new | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 11 deletions(-) create mode 100755 kiss-new diff --git a/kiss b/kiss index 3a2ced7..343d5db 100755 --- a/kiss +++ b/kiss @@ -3,15 +3,21 @@ # kiss - package manager for kiss linux. die() { + # Print a message and exit with '1' (error). printf '\033[31m!>\033[m %s\n' "$@" >&2 exit 1 } log() { + # Print a message with a colorful arrow to distinguish + # from other output. printf '\033[32m=>\033[m %s\n' "$@" } source_type() { + # Figure out what kind of source we are dealing with. + # This removes the need to repeat these same tests + # in each function. [ -z "$1" ] && return 1 # No file. [ -f "$1" ] && return 2 # Local file. [ -f "$src_dir/${1##*/}" ] && return 3 # Cached downloaded file. @@ -20,32 +26,44 @@ source_type() { } pkg_clean() { + # Clean up on exit or error. This removes everything related + # to the build. rm -rf -- "$mak_dir" "$pkg_dir" "$tar_dir" \ "$cac_dir/manifest-$$" "$cac_dir/checksums-$$" \ "$cac_dir/mv" "$cac_dir/mkdir" "$cac_dir/find" } pkg_search() { - set -f + # Figure out which repository a package belongs to by + # searching for directories matching the package name + # in $KISS_PATH. + + # Disable globbing with 'set -f' to ensure that the unquoted + # variable doesn't expand to anything nasty. # shellcheck disable=2086,2046 - set -- "$1" $(IFS=:; find $KISS_PATH -maxdepth 1 -name "$1") - set +f + { + set -f + set -- "$1" $(IFS=:; find $KISS_PATH -maxdepth 1 -name "$1") + set +f + } [ -z "$2" ] && die "Package '$1' not in any repository." - rep_dir=${2%/$1} + printf '%s\n' "$2" } pkg_setup() { - pkg_search "$1" + # Check that each mandatory file in the package entry exists. + pkg_location=$(pkg_search "$1") - cd "$rep_dir/$1" || die "'$rep_dir/$1' not accessible" - [ -f sources ] || die "Sources file not found." - [ -x build ] || die "Build file not found or not executable." - [ -f licenses ] || die "License file not found or empty." + cd "$pkg_location" || die "'$pkg_location' not accessible" + [ -f sources ] || die "Sources file not found." + [ -x build ] || die "Build file not found or not executable." + [ -f licenses ] || die "License file not found or empty." - read -r ver rel < version || die "Version file not found." - pkg=${name:=$1}\#$ver-$rel.tar.gz + read -r pkg_ver pkg_rel < version || die "Version file not found." + pkg_name=$1 + pkg_tar=$name\#$ver-$rel.tar.gz } pkg_depends() { diff --git a/kiss-new b/kiss-new new file mode 100755 index 0000000..f53a7b1 --- /dev/null +++ b/kiss-new @@ -0,0 +1,92 @@ +#!/bin/sh -e +# +# This is a simple package manager written in POSIX 'sh' for +# KISS Linux utlizing the core unix utilites where needed. +# +# The script runs with 'set -e' enabled. It will exit on any +# non-zero return code. This ensures that no function continues +# if it fails at any point. +# +# Keep in mind that this involves extra code in the case where +# an error is optional or required. +# +# The code below conforms to shellcheck's rules. However, some +# lint errors *are* disabled as they relate to unexpected +# behavior (which we do expect). +# +# KISS is available under the MIT license. +# +# - Dylan Araps. + +die() { + # Print a message and exit with '1' (error). + printf '\033[31m!>\033[m %s\n' "$@" >&2 + exit 1 +} + +log() { + # Print a message with a colorful arrow to distinguish + # from other output. + printf '\033[32m=>\033[m %s\n' "$@" +} + +pkg_search() { + # Figure out which repository a package belongs to by + # searching for directories matching the package name + # in $KISS_PATH/*. + [ "$KISS_PATH" ] || \ + die "\$KISS_PATH needs to be set." \ + "Example: KISS_PATH=/packages/core:/packages/extra:/packages/xorg" \ + "Repositories will be searched in the configured order." \ + "The variable should work just like \$PATH." + + # Disable globbing with 'set -f' to ensure that the unquoted + # variable doesn't expand into anything nasty. + # shellcheck disable=2086,2046 + { + set -f + set -- "$1" $(IFS=:; find $KISS_PATH -maxdepth 1 -name "$1") + set +f + } + + # A package may also not be found due to a repository not being + # readable by the current user. Either way, we need to die here. + [ -z "$2" ] && die "Package '$1' not in any repository." + + printf '%s\n' "$2" +} + +args() { + # Parse script arguments manually. POSIX 'sh' has no 'getopts' + # or equivalent built in. + [ "$1" ] || { + log "$kiss [b|c|i|l|r|u] [pkg]" \ + "build: Build a package." \ + "checksum: Generate checksums." \ + "install: Install a package (Runs build if needed)." \ + "list: List packages." \ + "remove: Remove a package." \ + "update: Check for updates." + exit + } + + action=$1 + shift + + while [ "$1" ]; do + case $action in + b*) + pkg_search "$1" + shift + ;; + esac + done +} + +main() { + kiss=${0##*/} + + args "$@" +} + +main "$@" From f2804c2b66824fdafe332c4723fbed989821c256 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 01:33:02 +0300 Subject: [PATCH 02/43] kiss-new: Argument handling --- kiss-new | 89 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 16 deletions(-) diff --git a/kiss-new b/kiss-new index f53a7b1..ea11394 100755 --- a/kiss-new +++ b/kiss-new @@ -10,6 +10,9 @@ # Keep in mind that this involves extra code in the case where # an error is optional or required. # +# Where possible the package manager should "error first". +# Check things first, die is necessary and continue if all is well. +# # The code below conforms to shellcheck's rules. However, some # lint errors *are* disabled as they relate to unexpected # behavior (which we do expect). @@ -56,28 +59,82 @@ pkg_search() { printf '%s\n' "$2" } +pkg_list() { + # List installed packages. As the format is files and + # diectories, this just involves a simple for loop and + # file read. + + # Changing directories is similar to storing the full + # full path in a variable, only there is no variable as + # you can access children relatively. + cd "$KISS_ROOT/var/db/kiss" || \ + die "KISS database doesn't exist or is inaccessible." + + # Optional arguments can be passed to check for specific + # packages. If no arguments are passed, list all. As we + # loop over '$@', if there aren't any arguments we can + # just set the directory contents to the argument list. + [ "$1" ] || set -- * + + # Loop over each version file and warn if one doesn't exist. + # Supress errors from 'read' as we print our own message. + for pkg; do + [ -d "$pkg" ] || { + log "Package '$pkg' is not installed." + return 1 + } + + [ -f "$pkg/version" ] || { + log "Warning: Package '$pkg' has no version file." + return + } + + read -r version release < "$pkg/version" && + printf '%s\n' "${pkg%/*} $version-$release" + done +} + args() { # Parse script arguments manually. POSIX 'sh' has no 'getopts' - # or equivalent built in. - [ "$1" ] || { - log "$kiss [b|c|i|l|r|u] [pkg]" \ - "build: Build a package." \ - "checksum: Generate checksums." \ - "install: Install a package (Runs build if needed)." \ - "list: List packages." \ - "remove: Remove a package." \ - "update: Check for updates." - exit - } - - action=$1 - shift + # or equivalent built in. This is rather easy to do in our case + # since the first argument is always an "action" and the arguments + # that follow are all package names. + # Actions can be abbreviated to their first letter. This saves + # keystrokes once you memorize themand it also has the side-effect + # of "correcting" spelling mistakes assuming the first letter is + # right. while [ "$1" ]; do - case $action in + case $1 in + # Build the list of packages. b*) - pkg_search "$1" + + ;; + + # List installed packages. + l*) shift + pkg_list "$@" + exit + ;; + + # Print version and exit. + v*) + log "$kiss 0.1.10" + exit + ;; + + # Catch all invalid arguments as well as + # any help related flags (-h, --help, help). + *) + log "$kiss [b|c|i|l|r|u] [pkg]" \ + "build: Build a package." \ + "checksum: Generate checksums." \ + "install: Install a package (Runs build if needed)." \ + "list: List packages." \ + "remove: Remove a package." \ + "update: Check for updates." + exit ;; esac done From 0e6f0c701a9d2952152f8c078f155116e2f4c551 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 01:35:29 +0300 Subject: [PATCH 03/43] kiss-new: Argument handling --- kiss-new | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kiss-new b/kiss-new index ea11394..bbb4c44 100755 --- a/kiss-new +++ b/kiss-new @@ -77,7 +77,7 @@ pkg_list() { [ "$1" ] || set -- * # Loop over each version file and warn if one doesn't exist. - # Supress errors from 'read' as we print our own message. + # Also warn if a package is missing its version file. for pkg; do [ -d "$pkg" ] || { log "Package '$pkg' is not installed." From ac0b0b7deb4ef9de2eed7b9b117e7cdbe919498a Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 01:36:54 +0300 Subject: [PATCH 04/43] kiss-new: Fix non args. --- kiss-new | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kiss-new b/kiss-new index bbb4c44..2667553 100755 --- a/kiss-new +++ b/kiss-new @@ -104,7 +104,7 @@ args() { # keystrokes once you memorize themand it also has the side-effect # of "correcting" spelling mistakes assuming the first letter is # right. - while [ "$1" ]; do + while [ "${1:-?}" ]; do case $1 in # Build the list of packages. b*) From 9282240df6698a17473c37def5109765962555cf Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 09:15:18 +0300 Subject: [PATCH 05/43] kiss-new: progress. --- kiss-new | 164 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 134 insertions(+), 30 deletions(-) diff --git a/kiss-new b/kiss-new index 2667553..73f10d6 100755 --- a/kiss-new +++ b/kiss-new @@ -33,6 +33,20 @@ log() { printf '\033[32m=>\033[m %s\n' "$@" } +pkg_lint() { + # Check that each mandatory file in the package entry exists. + log "[$1]: Checking repository files..." + + pkg_location=$(pkg_search "$1") + + cd "$pkg_location" || die "'$pkg_location' not accessible" + + [ -f sources ] || die "Sources file not found." + [ -x build ] || die "Build file not found or not executable." + [ -f licenses ] || die "License file not found or empty." + [ -f version ] || die "Version file not found or empty." +} + pkg_search() { # Figure out which repository a package belongs to by # searching for directories matching the package name @@ -94,6 +108,79 @@ pkg_list() { done } +pkg_sources() { + # Download any remote package sources. + log "[$1]: Downloading sources..." + + # Store each downloaded source in named after the package it + # belongs to. This avoid conflicts between two packages having a + # source of the same name. + mkdir -p "$src_dir/$1" && cd "$src_dir/$1" + + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$1") + + while read -r src _; do + case $src in + # Git repository. + git:*) + git clone "${src##git:}" "$mak_dir" + ;; + + # Remote source. + *://*) + [ -f "${src##*/}" ] && { + log "[$1]: Found cached source '${src##*/}'." + continue + } + + wget "$src" || die "[$1]: Failed to download $src." + ;; + + # Local files (Any source that is non-remote is assumed to be local). + *) + [ -f "$repo_dir/$src" ] || + die "[$1]: No local file '$src'." + + log "[$1]: Found local file '$src'." + ;; + esac + done < "$repo_dir/sources" +} + +pkg_checksums() { + for pkg; do pkg_lint "$pkg"; done + for pkg; do pkg_sources "$pkg"; done +} + +setup_caching() { + # Setup the host machine for the package manager. Create any + # directories which need to exist and set variables for easy + # access to them. + + # Main cache directory (~/.cache/kiss/) typically. + mkdir -p "${cac_dir:=${XDG_CACHE_HOME:=$HOME/.cache}/$kiss}" || + die "Couldn't create cache directory ($cac_dir)." + + # Build directory. + mkdir -p "${mak_dir:=$cac_dir/build-$$}" || + die "Couldn't create build directory ($mak_dir)." + + # Binary directory. + mkdir -p "${bin_dir:=$cac_dir/bin}" || + die "Couldn't create binary directory ($bin_dir)." + + # Tar directory. + mkdir -p "${tar_dir:=$cac_dir/extract-$$}" || + die "Couldn't create tar directory ($tar_dir)." + + # Source directory. + mkdir -p "${src_dir:=$cac_dir/sources}" || + die "Couldn't create source directory ($src_dir)." +} + args() { # Parse script arguments manually. POSIX 'sh' has no 'getopts' # or equivalent built in. This is rather easy to do in our case @@ -104,45 +191,62 @@ args() { # keystrokes once you memorize themand it also has the side-effect # of "correcting" spelling mistakes assuming the first letter is # right. - while [ "${1:-?}" ]; do - case $1 in - # Build the list of packages. - b*) + case $1 in + # Build the list of packages. + b*) - ;; + ;; - # List installed packages. - l*) - shift - pkg_list "$@" - exit - ;; + # Generate checksums for packages. + c*) + shift + [ "$1" ] || die "'kiss checksum' requires an argument." + pkg_checksums "$@" + exit + ;; - # Print version and exit. - v*) - log "$kiss 0.1.10" - exit - ;; + # List installed packages. + l*) + shift + pkg_list "$@" + exit + ;; - # Catch all invalid arguments as well as - # any help related flags (-h, --help, help). - *) - log "$kiss [b|c|i|l|r|u] [pkg]" \ - "build: Build a package." \ - "checksum: Generate checksums." \ - "install: Install a package (Runs build if needed)." \ - "list: List packages." \ - "remove: Remove a package." \ - "update: Check for updates." - exit - ;; - esac - done + # Print version and exit. + v*) + log "$kiss 0.1.10" + exit + ;; + + # Catch all invalid arguments as well as + # any help related flags (-h, --help, help). + *) + log "$kiss [b|c|i|l|r|u] [pkg]" \ + "build: Build a package." \ + "checksum: Generate checksums." \ + "install: Install a package (Runs build if needed)." \ + "list: List packages." \ + "remove: Remove a package." \ + "update: Check for updates." + exit + ;; + esac } main() { + # Store the script name in a variable and use it everywhere + # in place of 'kiss'. This allows the script name to be changed + # easily. kiss=${0##*/} + # The PID of the current shell process is used to isolate directories + # to each specific KISS instance. This allows multiple package manager + # instances to be run at once. Store the value in another variable so + # that it doesn't change beneath us. + pid=$$ + + setup_caching + args "$@" } From b322ed8c40111b5757de1e129bf4b0c4c2a9b1e2 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 09:31:46 +0300 Subject: [PATCH 06/43] kiss-new: Added checksum generation. --- kiss-new | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/kiss-new b/kiss-new index 73f10d6..47ef685 100755 --- a/kiss-new +++ b/kiss-new @@ -151,8 +151,51 @@ pkg_sources() { } pkg_checksums() { + # Generate checksums for a package. + # This also downloads any remote sources. for pkg; do pkg_lint "$pkg"; done for pkg; do pkg_sources "$pkg"; done + + for pkg; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + while read -r src _; do + case $src in + # Git repository. + # Skip checksums on git repositories. + git:*) ;; + + *) + # File is local to the package and is stored in the + # repository. + [ -f "$repo_dir/$src" ] && + src_path=$repo_dir/${src%/*} + + # File is remote and was downloaded. + [ -f "$src_dir/$pkg/${src##*/}" ] && + src_path=$src_dir/$pkg + + # Die here if source for some reason, doesn't exist. + [ "$src_path" ] || + die "[$pkg]: Couldn't find source '$src'." + + # An easy way to get 'sha256sum' to print with the basenames + # of files is to 'cd' to the file's directory beforehand. + (cd "$src_path" && sha256sum "${src##*/}") || + die "[$pkg]: Failed to generate checksums." + + # Unset this variable so it isn't used again on a failed + # source. There's no 'local' keyword in POSIX sh. + src_path= + ;; + esac + done < "$repo_dir/sources" > "$repo_dir/checksums" + + log "[$pkg]: Generated checksums." + done } setup_caching() { From add5b983681a6a6a7371d34af2c550773be2431e Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 09:40:03 +0300 Subject: [PATCH 07/43] kiss-new: Catch errors. --- kiss-new | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/kiss-new b/kiss-new index 47ef685..f9df047 100755 --- a/kiss-new +++ b/kiss-new @@ -41,10 +41,10 @@ pkg_lint() { cd "$pkg_location" || die "'$pkg_location' not accessible" - [ -f sources ] || die "Sources file not found." - [ -x build ] || die "Build file not found or not executable." - [ -f licenses ] || die "License file not found or empty." - [ -f version ] || die "Version file not found or empty." + [ -f sources ] || die "Sources file not found." + [ -x build ] || die "Build file not found or not executable." + [ -f licenses ] || die "License file not found or empty." + [ -f version ] || die "Version file not found or empty." } pkg_search() { @@ -208,20 +208,30 @@ setup_caching() { die "Couldn't create cache directory ($cac_dir)." # Build directory. - mkdir -p "${mak_dir:=$cac_dir/build-$$}" || + mkdir -p "${mak_dir:=$cac_dir/build-$pid}" || die "Couldn't create build directory ($mak_dir)." - # Binary directory. - mkdir -p "${bin_dir:=$cac_dir/bin}" || - die "Couldn't create binary directory ($bin_dir)." + # Package directory. + mkdir -p "${pkg_dir:=$cac_dir/pkg-$pid}" || + die "Couldn't create package directory ($pkg_dir)." # Tar directory. - mkdir -p "${tar_dir:=$cac_dir/extract-$$}" || + mkdir -p "${tar_dir:=$cac_dir/extract-$pid}" || die "Couldn't create tar directory ($tar_dir)." # Source directory. mkdir -p "${src_dir:=$cac_dir/sources}" || die "Couldn't create source directory ($src_dir)." + + # Binary directory. + mkdir -p "${bin_dir:=$cac_dir/bin}" || + die "Couldn't create binary directory ($bin_dir)." +} + +pkg_clean() { + # Clean up on exit or error. This removes everything related + # to the build. + rm -rf -- "$mak_dir" "$pkg_dir" "$tar_dir" } args() { @@ -288,8 +298,11 @@ main() { # that it doesn't change beneath us. pid=$$ - setup_caching + # Catch errors and ensure that build files and directories are cleaned + # up before we die. This occurs on 'Ctrl+C' as well as sucess and error. + trap pkg_clean EXIT INT + setup_caching args "$@" } From 56bf33c6849146f1a8b360e05e8c0f85f2b54269 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 09:46:32 +0300 Subject: [PATCH 08/43] kiss-new: Clean up --- kiss-new | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/kiss-new b/kiss-new index f9df047..1dda4ac 100755 --- a/kiss-new +++ b/kiss-new @@ -247,7 +247,8 @@ args() { case $1 in # Build the list of packages. b*) - + shift + [ "$1" ] || die "'kiss build' requires an argument." ;; # Generate checksums for packages. @@ -255,20 +256,17 @@ args() { shift [ "$1" ] || die "'kiss checksum' requires an argument." pkg_checksums "$@" - exit ;; # List installed packages. l*) shift pkg_list "$@" - exit ;; # Print version and exit. v*) log "$kiss 0.1.10" - exit ;; # Catch all invalid arguments as well as @@ -281,7 +279,6 @@ args() { "list: List packages." \ "remove: Remove a package." \ "update: Check for updates." - exit ;; esac } From 31225ae542fe0ba5cb9830a4eb6a6822dc4870d0 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 09:53:37 +0300 Subject: [PATCH 09/43] kiss-new: Added root check. --- kiss-new | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/kiss-new b/kiss-new index 1dda4ac..69ba4c2 100755 --- a/kiss-new +++ b/kiss-new @@ -150,8 +150,13 @@ pkg_sources() { done < "$repo_dir/sources" } +pkg_build() { + # Build packages. + : +} + pkg_checksums() { - # Generate checksums for a package. + # Generate checksums for packages. # This also downloads any remote sources. for pkg; do pkg_lint "$pkg"; done for pkg; do pkg_sources "$pkg"; done @@ -234,6 +239,14 @@ pkg_clean() { rm -rf -- "$mak_dir" "$pkg_dir" "$tar_dir" } +root_check() { + # Ensure that the user has write permissions to '$KISS_ROOT'. + # When this variable is empty, a value of '/' is assumed. + [ -w "$KISS_ROOT/" ] || \ + die "No write permissions to '${KISS_ROOT:-/}'." \ + "You may need to run '$kiss' as root." +} + args() { # Parse script arguments manually. POSIX 'sh' has no 'getopts' # or equivalent built in. This is rather easy to do in our case @@ -249,6 +262,7 @@ args() { b*) shift [ "$1" ] || die "'kiss build' requires an argument." + pkg_build "$@" ;; # Generate checksums for packages. @@ -258,6 +272,20 @@ args() { pkg_checksums "$@" ;; + # Install packages. + i*) + shift + [ "$1" ] || die "'kiss install' requires an argument." + root_check + ;; + + # Remove packages. + r*) + shift + [ "$1" ] || die "'kiss remove' requires an argument." + root_check + ;; + # List installed packages. l*) shift From 416f2b097b192b8d626c6221ede9e1af7b83f994 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 10:02:43 +0300 Subject: [PATCH 10/43] kiss-new: Ensure that each package has checksums. --- kiss-new | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/kiss-new b/kiss-new index 69ba4c2..1fe6de0 100755 --- a/kiss-new +++ b/kiss-new @@ -151,8 +151,30 @@ pkg_sources() { } pkg_build() { - # Build packages. - : + # Build packages and turn them into packaged tarballs. This function + # also checks checksums, downloads sources and ensure all dependencies + # are installed. + + for pkg; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + # Ensure that checksums exist prior to building the package. + [ -f "$repo_dir/checksums" ] || { + log "[$pkg]: Checksums are missing." + + # Instead of dying above, log it to the terminal. Also define a + # variable so we *can* die after all checksum files have been + # checked. + no_checkums="$no_checkums$pkg " + } + done + + # Die here as packages without checksums were found above. + [ "$no_checkums" ] && + die "Run '$kiss checksum ${no_checkums% }' to generate checksums." } pkg_checksums() { From 2698a51f5e4c7ec977c67bac8078ecbfe3e341c6 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 10:11:07 +0300 Subject: [PATCH 11/43] kiss-new: Added more checks to the linter. --- kiss-new | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/kiss-new b/kiss-new index 1fe6de0..f229c0c 100755 --- a/kiss-new +++ b/kiss-new @@ -43,8 +43,17 @@ pkg_lint() { [ -f sources ] || die "Sources file not found." [ -x build ] || die "Build file not found or not executable." - [ -f licenses ] || die "License file not found or empty." - [ -f version ] || die "Version file not found or empty." + [ -s licenses ] || die "License file not found or empty." + [ -s version ] || die "Version file not found or empty." + + # Ensure that the release field in the version file is set + # to something. + read -r _ rel < version + [ "$rel" ] || die "Release field not found in version file." + + # Unset this variable so it isn't used again on a failed + # source. There's no 'local' keyword in POSIX sh. + rel= } pkg_search() { From 4a84fe78f5b1761f808978119b8ef90e227c2943 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 10:42:27 +0300 Subject: [PATCH 12/43] kiss-new: Initial dependency solver --- kiss-new | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/kiss-new b/kiss-new index f229c0c..c4916e0 100755 --- a/kiss-new +++ b/kiss-new @@ -103,7 +103,7 @@ pkg_list() { # Also warn if a package is missing its version file. for pkg; do [ -d "$pkg" ] || { - log "Package '$pkg' is not installed." + log "Package '$pkg' is not installed." >&2 return 1 } @@ -159,6 +159,32 @@ pkg_sources() { done < "$repo_dir/sources" } +pkg_depends() { + # Resolve all dependencies and install them in the right order. + for pkg; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + # Package doesn't depend on anything, skip it. + [ -f "$repo_dir/depends" ] || continue + + while read -r dep _; do + pkg_list "$dep" >/dev/null || { + case $missing_deps in + # Dependency is already in list, skip it. + *" $dep "*) ;; + + *) + missing_deps="$missing_deps $dep " + ;; + esac + } + done < "$repo_dir/depends" + done +} + pkg_build() { # Build packages and turn them into packaged tarballs. This function # also checks checksums, downloads sources and ensure all dependencies @@ -184,6 +210,8 @@ pkg_build() { # Die here as packages without checksums were found above. [ "$no_checkums" ] && die "Run '$kiss checksum ${no_checkums% }' to generate checksums." + + pkg_depends "$@" } pkg_checksums() { From f6d7a387eac4e682136a9a83d4111081f504ce4d Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 10:56:39 +0300 Subject: [PATCH 13/43] kiss-new: Full dependency resolvers. --- kiss-new | 55 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/kiss-new b/kiss-new index c4916e0..d9576a3 100755 --- a/kiss-new +++ b/kiss-new @@ -103,7 +103,7 @@ pkg_list() { # Also warn if a package is missing its version file. for pkg; do [ -d "$pkg" ] || { - log "Package '$pkg' is not installed." >&2 + log "Package '$pkg' is not installed." return 1 } @@ -161,28 +161,27 @@ pkg_sources() { pkg_depends() { # Resolve all dependencies and install them in the right order. - for pkg; do - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$pkg") - # Package doesn't depend on anything, skip it. - [ -f "$repo_dir/depends" ] || continue + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$1") - while read -r dep _; do - pkg_list "$dep" >/dev/null || { - case $missing_deps in - # Dependency is already in list, skip it. - *" $dep "*) ;; + pkg_list "$1" >/dev/null || { + case $missing_deps in + # Dependency is already in list, skip it. + *" $1 "*) ;; - *) - missing_deps="$missing_deps $dep " - ;; - esac - } - done < "$repo_dir/depends" - done + *) + [ -f "$repo_dir/depends" ] && + while read -r dep _; do + pkg_depends "$dep" + done < "$repo_dir/depends" + + missing_deps="$missing_deps $1 " + ;; + esac + } } pkg_build() { @@ -190,6 +189,20 @@ pkg_build() { # also checks checksums, downloads sources and ensure all dependencies # are installed. + # Resolve dependencies and generate a list. + log "Resolving dependencies..." + for pkg; do pkg_depends "$pkg"; done + + # Disable globbing with 'set -f' to ensure that the unquoted + # variable doesn't expand into anything nasty. + # shellcheck disable=2086,2046 + { + # Set the resolved dependency list as the function's arguments. + set -f + set -- $missing_deps + set +f + } + for pkg; do # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data @@ -210,8 +223,6 @@ pkg_build() { # Die here as packages without checksums were found above. [ "$no_checkums" ] && die "Run '$kiss checksum ${no_checkums% }' to generate checksums." - - pkg_depends "$@" } pkg_checksums() { From e8a44649234a1d8d98fca043e7ff1bf8e966f5db Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 11:02:44 +0300 Subject: [PATCH 14/43] kiss-new: Get all package sources for build. --- kiss-new | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kiss-new b/kiss-new index d9576a3..77d25ef 100755 --- a/kiss-new +++ b/kiss-new @@ -202,6 +202,7 @@ pkg_build() { set -- $missing_deps set +f } + log "Installing: $*." for pkg; do # Find the package's repository files. This needs to keep @@ -223,6 +224,8 @@ pkg_build() { # Die here as packages without checksums were found above. [ "$no_checkums" ] && die "Run '$kiss checksum ${no_checkums% }' to generate checksums." + + for pkg; do pkg_sources "$pkg"; done } pkg_checksums() { From 29b4406e34bb7c64cc625df44f2ddc799a5ec61e Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 11:16:12 +0300 Subject: [PATCH 15/43] kiss-new: Started work on package verification. --- kiss-new | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/kiss-new b/kiss-new index 77d25ef..9ad9a59 100755 --- a/kiss-new +++ b/kiss-new @@ -145,7 +145,10 @@ pkg_sources() { continue } - wget "$src" || die "[$1]: Failed to download $src." + wget "$src" || { + rm -f "${src##*/}" + die "[$1]: Failed to download $src." + } ;; # Local files (Any source that is non-remote is assumed to be local). @@ -204,6 +207,8 @@ pkg_build() { } log "Installing: $*." + for pkg; do pkg_lint "$pkg"; done + for pkg; do # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data @@ -226,14 +231,33 @@ pkg_build() { die "Run '$kiss checksum ${no_checkums% }' to generate checksums." for pkg; do pkg_sources "$pkg"; done + + for pkg; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + pkg_checksums "$pkg" + + cmp -s "$cac_dir/checksums-$pkg" "$repo_dir/checksums" || { + log "[$pkg]: Checksum mismatch." + + # Instead of dying above, log it to the terminal. Also define a + # variable so we *can* die after all checksum files have been + # checked. + mismatch="$mismatch$pkg " + } + done + + # Die here as packages with differing checksums were found above. + [ "$mismatch" ] && + die "Checksum mismatch with: ${mismatch% }" } pkg_checksums() { # Generate checksums for packages. # This also downloads any remote sources. - for pkg; do pkg_lint "$pkg"; done - for pkg; do pkg_sources "$pkg"; done - for pkg; do # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data @@ -270,7 +294,8 @@ pkg_checksums() { src_path= ;; esac - done < "$repo_dir/sources" > "$repo_dir/checksums" + done < "$repo_dir/sources" | \ + tee "$cac_dir/checksums-$pkg" > "$repo_dir/checksums" log "[$pkg]: Generated checksums." done @@ -309,7 +334,12 @@ setup_caching() { pkg_clean() { # Clean up on exit or error. This removes everything related # to the build. + + # Remove temporary directories. rm -rf -- "$mak_dir" "$pkg_dir" "$tar_dir" + + # Remove temporary checksum files. + rm -rf -- "$cac_dir/checksums-"* } root_check() { @@ -342,6 +372,10 @@ args() { c*) shift [ "$1" ] || die "'kiss checksum' requires an argument." + + for pkg; do pkg_lint "$pkg"; done + for pkg; do pkg_sources "$pkg"; done + pkg_checksums "$@" ;; From c43a50b12c034cdcb531d518e9c532c12da16d0b Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 11:18:30 +0300 Subject: [PATCH 16/43] kiss-new: Added TODO comment. --- kiss-new | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kiss-new b/kiss-new index 9ad9a59..3c5af1c 100755 --- a/kiss-new +++ b/kiss-new @@ -232,6 +232,9 @@ pkg_build() { for pkg; do pkg_sources "$pkg"; done + # TODO: Fix this. This doesn't *actually* work as it overwrites + # the repository checksums. 'pkg_checksum' needs to be modified + # to support a destination file as output. for pkg; do # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data From afdb873c05214a4b9c877e892d354b38b4e118d3 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 16:40:03 +0300 Subject: [PATCH 17/43] kiss-new: Verify checksums. --- kiss-new | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/kiss-new b/kiss-new index 3c5af1c..250f8fd 100755 --- a/kiss-new +++ b/kiss-new @@ -170,7 +170,13 @@ pkg_depends() { # structure. repo_dir=$(pkg_search "$1") - pkg_list "$1" >/dev/null || { + # This does a depth-first search. The deepest dependencies are + # listed first and then the parents in reverse order. + if pkg_list "$1" >/dev/null; then + # If a package is already installed but 'pkg_depends' was + # given an argument, add it to the list anyway. + [ "$2" ] && missing_deps="$missing_deps $1 " + else case $missing_deps in # Dependency is already in list, skip it. *" $1 "*) ;; @@ -184,7 +190,7 @@ pkg_depends() { missing_deps="$missing_deps $1 " ;; esac - } + fi } pkg_build() { @@ -193,8 +199,10 @@ pkg_build() { # are installed. # Resolve dependencies and generate a list. + # Send 'force' to 'pkg_depends' to always include the explicitly + # requested packages. log "Resolving dependencies..." - for pkg; do pkg_depends "$pkg"; done + for pkg; do pkg_depends "$pkg" force; done # Disable globbing with 'set -f' to ensure that the unquoted # variable doesn't expand into anything nasty. @@ -208,7 +216,6 @@ pkg_build() { log "Installing: $*." for pkg; do pkg_lint "$pkg"; done - for pkg; do # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data @@ -231,19 +238,18 @@ pkg_build() { die "Run '$kiss checksum ${no_checkums% }' to generate checksums." for pkg; do pkg_sources "$pkg"; done - - # TODO: Fix this. This doesn't *actually* work as it overwrites - # the repository checksums. 'pkg_checksum' needs to be modified - # to support a destination file as output. for pkg; do # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data # structure. repo_dir=$(pkg_search "$pkg") - pkg_checksums "$pkg" + # Generate a second set of checksums to compare against the + # repositorie's checksums for the package. + pkg_checksums .checksums "$pkg" - cmp -s "$cac_dir/checksums-$pkg" "$repo_dir/checksums" || { + # Compare the checksums using 'cmp'. + cmp -s "$repo_dir/.checksums" "$repo_dir/checksums" || { log "[$pkg]: Checksum mismatch." # Instead of dying above, log it to the terminal. Also define a @@ -251,16 +257,24 @@ pkg_build() { # checked. mismatch="$mismatch$pkg " } + + # The second set of checksums use a temporary file, we need to + # delete it. + rm -f "$repo_dir/.checksums" done # Die here as packages with differing checksums were found above. - [ "$mismatch" ] && - die "Checksum mismatch with: ${mismatch% }" + [ "$mismatch" ] && die "Checksum mismatch with: ${mismatch% }" + + log "Verified all checksums." } pkg_checksums() { # Generate checksums for packages. # This also downloads any remote sources. + checksum_file=$1 + shift + for pkg; do # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data @@ -297,10 +311,9 @@ pkg_checksums() { src_path= ;; esac - done < "$repo_dir/sources" | \ - tee "$cac_dir/checksums-$pkg" > "$repo_dir/checksums" + done < "$repo_dir/sources" > "$repo_dir/$checksum_file" - log "[$pkg]: Generated checksums." + log "[$pkg]: Generated/Verified checksums." done } @@ -379,7 +392,7 @@ args() { for pkg; do pkg_lint "$pkg"; done for pkg; do pkg_sources "$pkg"; done - pkg_checksums "$@" + pkg_checksums checksums "$@" ;; # Install packages. From 8e92589eaa6b269cae34baf0dd3cee3aae3c0c3d Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 17:01:47 +0300 Subject: [PATCH 18/43] kiss-new: Added pkg_extract --- kiss-new | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/kiss-new b/kiss-new index 250f8fd..166d144 100755 --- a/kiss-new +++ b/kiss-new @@ -162,6 +162,47 @@ pkg_sources() { done < "$repo_dir/sources" } +pkg_extract() { + # Extract all source archives to the build diectory and copy over + # any local repository files. + log "[$1]: Extracting sources..." + + # Store each downloaded source in named after the package it + # belongs to. This avoid conflicts between two packages having a + # source of the same name. + mkdir -p "$mak_dir/$1" && cd "$mak_dir/$1" + + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$1") + + while read -r src dest; do + mkdir -p "./$dest" + + case $src in + # Do nothing as git repository was downloaded to the build + # diectory directly. + git:*) ;; + + # Only 'tar' archives are currently supported for extaction. + # Any other filetypes are simply copied to '$mak_dir' which + # allows you to extract them manually. + *://*.tar*|*://*.tgz) + tar xf "$src_dir/$1/${src##*/}" -C "./$dest" \ + --strip-components 1 \ + || die "[$1]: Couldn't extract ${src##*/}." + ;; + + # Local files (Any source that is non-remote is assumed to be local). + *) + [ -f "$repo_dir/$src" ] || die "[$1]: Local file $src not found." + cp -f "$repo_dir/$src" "./$dest" + ;; + esac + done < "$repo_dir/sources" +} + pkg_depends() { # Resolve all dependencies and install them in the right order. @@ -267,6 +308,9 @@ pkg_build() { [ "$mismatch" ] && die "Checksum mismatch with: ${mismatch% }" log "Verified all checksums." + + for pkg; do pkg_extract "$pkg"; done + log "Extracted all sources." } pkg_checksums() { From 324f1d3fb031893338b5d539323c6cbc70dfd822 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 17:09:47 +0300 Subject: [PATCH 19/43] kiss-new: Build packages. --- kiss-new | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/kiss-new b/kiss-new index 166d144..64833b8 100755 --- a/kiss-new +++ b/kiss-new @@ -311,6 +311,28 @@ pkg_build() { for pkg; do pkg_extract "$pkg"; done log "Extracted all sources." + + log "Building packages..." + for pkg; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + # Move to the build directory and call the build script. + (cd "$mak_dir/$pkg"; "$repo_dir/build" "$pkg_dir") || + die "[$pkg]: Build failed." + + # Copy the repository files to the package directory. + # This acts as the database entry. + cp -Rf "$repo_dir" "$pkg_db" + + log "[$pkg]: Sucessfully built package." + + # Create the manifest file early and make it empty. + # This ensure that the manifest is added to the manifest... + : > "$pkg_db/$pkg/manifest" + done } pkg_checksums() { @@ -375,7 +397,7 @@ setup_caching() { die "Couldn't create build directory ($mak_dir)." # Package directory. - mkdir -p "${pkg_dir:=$cac_dir/pkg-$pid}" || + mkdir -p "${pkg_db:=${pkg_dir:=$cac_dir/pkg-$pid}/var/db/$kiss}" || die "Couldn't create package directory ($pkg_dir)." # Tar directory. From 77e1fa35c60ef0e401fea44715dc6e120f073cb1 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 17:16:16 +0300 Subject: [PATCH 20/43] kiss-new: Fix set -e error. --- kiss-new | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kiss-new b/kiss-new index 64833b8..d0d88b5 100755 --- a/kiss-new +++ b/kiss-new @@ -225,7 +225,7 @@ pkg_depends() { *) [ -f "$repo_dir/depends" ] && while read -r dep _; do - pkg_depends "$dep" + pkg_depends "$dep" ||: done < "$repo_dir/depends" missing_deps="$missing_deps $1 " From 7f903ff68291b465166e0e786d444e913c4b2d32 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 17:20:29 +0300 Subject: [PATCH 21/43] kiss-new: Remove unneeded code. --- kiss-new | 3 --- 1 file changed, 3 deletions(-) diff --git a/kiss-new b/kiss-new index d0d88b5..3449306 100755 --- a/kiss-new +++ b/kiss-new @@ -419,9 +419,6 @@ pkg_clean() { # Remove temporary directories. rm -rf -- "$mak_dir" "$pkg_dir" "$tar_dir" - - # Remove temporary checksum files. - rm -rf -- "$cac_dir/checksums-"* } root_check() { From 303a80c9d93d1134ed5a74c39215bb290fd400cf Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 18:09:07 +0300 Subject: [PATCH 22/43] kiss-new: Added package stripping. --- kiss-new | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/kiss-new b/kiss-new index 3449306..c9befe5 100755 --- a/kiss-new +++ b/kiss-new @@ -319,20 +319,54 @@ pkg_build() { # structure. repo_dir=$(pkg_search "$pkg") + # Install built packages to a directory under the package name + # to avod collisions with other packages. + mkdir -p "$pkg_dir/$pkg/var/db/$kiss" + # Move to the build directory and call the build script. - (cd "$mak_dir/$pkg"; "$repo_dir/build" "$pkg_dir") || + (cd "$mak_dir/$pkg"; "$repo_dir/build" "$pkg_dir/$pkg") || die "[$pkg]: Build failed." # Copy the repository files to the package directory. # This acts as the database entry. - cp -Rf "$repo_dir" "$pkg_db" + cp -Rf "$repo_dir" "$pkg_dir/$pkg/var/db/$kiss" log "[$pkg]: Sucessfully built package." # Create the manifest file early and make it empty. # This ensure that the manifest is added to the manifest... - : > "$pkg_db/$pkg/manifest" + : > "$pkg_dir/$pkg/var/db/$kiss/$pkg/manifest" done + + log "Stripping packages..." + for pkg; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + # Package has stripping disabled, stop here. + [ -f "$repo_dir/nostrip" ] && continue + + log "[$pkg]: Stripping binaries and libraries..." + + find "$pkg_dir/$pkg" -type f | while read -r binary; do + case $(file -bi "$binary") in + application/x-sharedlib*|application/x-pie-executable*) + strip_opts=--strip-unneeded + ;; + + application/x-archive*) strip_opts=--strip-debug ;; + application/x-executable*) strip_opts=--strip-all ;; + + *) continue ;; + esac + + strip "$strip_opts" "$binary" 2>/dev/null + done + done + + log "Stripped all binaries and libraries." } pkg_checksums() { @@ -397,7 +431,7 @@ setup_caching() { die "Couldn't create build directory ($mak_dir)." # Package directory. - mkdir -p "${pkg_db:=${pkg_dir:=$cac_dir/pkg-$pid}/var/db/$kiss}" || + mkdir -p "${pkg_dir:=$cac_dir/pkg-$pid}" || die "Couldn't create package directory ($pkg_dir)." # Tar directory. From d334334631c388caf40c7566d14793f15befac07 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 18:20:53 +0300 Subject: [PATCH 23/43] kiss-new: Added manifests. --- kiss-new | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/kiss-new b/kiss-new index c9befe5..ae0e933 100755 --- a/kiss-new +++ b/kiss-new @@ -329,7 +329,7 @@ pkg_build() { # Copy the repository files to the package directory. # This acts as the database entry. - cp -Rf "$repo_dir" "$pkg_dir/$pkg/var/db/$kiss" + cp -Rf "$repo_dir" "$pkg_dir/$pkg/var/db/$kiss/" log "[$pkg]: Sucessfully built package." @@ -367,6 +367,19 @@ pkg_build() { done log "Stripped all binaries and libraries." + log "Generating package manifests..." + + for pkg; do + # This runs in a subshell so we can avoid a 'cd -' or 'cd $OLDPWD'. + ( + cd "$pkg_dir/$pkg" + find . -type d -exec printf '%s/\n' {} + -or -print + ) | sort -r | sed -e ss.ss > "$pkg_dir/$pkg/var/db/$kiss/$pkg/manifest" + + log "[$pkg]: Generated manifest." + done + + log "Generated all manifests." } pkg_checksums() { From 06aaa4ece19b822cf24f026efd49028d000eb3c2 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 18:25:30 +0300 Subject: [PATCH 24/43] kiss-new: Create tarballs. --- kiss-new | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/kiss-new b/kiss-new index ae0e933..1c7c454 100755 --- a/kiss-new +++ b/kiss-new @@ -380,6 +380,23 @@ pkg_build() { done log "Generated all manifests." + log "Creating package tarballs..." + + for pkg; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + # Read the version information to name the package. + read -r version release < "$repo_dir/version" + + # Create a tarball from the contents of the built package. + tar zpcf "$bin_dir/$pkg-$version-$release.tar.gz" -C "$pkg_dir/$pkg" . || + die "[$pkg]: Failed to create tarball." + done + + log "Created all packages." } pkg_checksums() { From bc3abc241ebd1ce506bf7056aeabb4cc8ddeed6c Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 19:19:18 +0300 Subject: [PATCH 25/43] kiss-new: Move things to functions. --- kiss-new | 181 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 97 insertions(+), 84 deletions(-) diff --git a/kiss-new b/kiss-new index 1c7c454..825cd75 100755 --- a/kiss-new +++ b/kiss-new @@ -87,17 +87,15 @@ pkg_list() { # diectories, this just involves a simple for loop and # file read. - # Changing directories is similar to storing the full - # full path in a variable, only there is no variable as - # you can access children relatively. - cd "$KISS_ROOT/var/db/kiss" || \ - die "KISS database doesn't exist or is inaccessible." - # Optional arguments can be passed to check for specific # packages. If no arguments are passed, list all. As we # loop over '$@', if there aren't any arguments we can # just set the directory contents to the argument list. - [ "$1" ] || set -- * + [ "$1" ] || set -- "$KISS_ROOT/var/db/$kiss/"* + + # If the 'glob' above failed, exit early as there are no + # packages installed. + [ "$1" = "$KISS_ROOT/var/db/$kiss/"\* ] && return # Loop over each version file and warn if one doesn't exist. # Also warn if a package is missing its version file. @@ -234,6 +232,92 @@ pkg_depends() { fi } +pkg_verify() { + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$1") + + # Generate a second set of checksums to compare against the + # repositorie's checksums for the package. + pkg_checksums .checksums "$1" + + # Compare the checksums using 'cmp'. + cmp -s "$repo_dir/.checksums" "$repo_dir/checksums" || { + log "[$1]: Checksum mismatch." + + # Instead of dying above, log it to the terminal. Also define a + # variable so we *can* die after all checksum files have been + # checked. + mismatch="$mismatch$1 " + } + + # The second set of checksums use a temporary file, we need to + # delete it. + rm -f "$repo_dir/.checksums" +} + +pkg_strip() { + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$1") + + # Package has stripping disabled, stop here. + [ -f "$repo_dir/nostrip" ] && return + + log "[$1]: Stripping binaries and libraries..." + + find "$pkg_dir/$1" -type f | while read -r binary; do + case $(file -bi "$binary") in + application/x-sharedlib*|application/x-pie-executable*) + strip_opts=--strip-unneeded + ;; + + application/x-archive*) strip_opts=--strip-debug ;; + application/x-executable*) strip_opts=--strip-all ;; + + *) continue ;; + esac + + strip "$strip_opts" "$binary" 2>/dev/null + done +} + +pkg_manifest() ( + # Generate the package's manifest file. This is a list of each file + # and directory inside the package. The file is used when uninstalling + # packages, checking for package conflicts and for general debugging. + # + # This funcion runs as a subshell to avoid having to 'cd' back to the + # prior directory before being able to continue. + cd "$pkg_dir/$1" + + # Find all files and directories in the package. Directories are printed + # with a trailing forward slash '/'. The list is then reversed with + # directories appearing *after* their contents. + find . -type d -exec printf '%s/\n' {} + -or -print | + sort -r | sed -e ss.ss > "$pkg_dir/$1/var/db/$kiss/$1/manifest" + + log "[$1]: Generated manifest." +) + +pkg_tar() { + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$1") + + # Read the version information to name the package. + read -r version release < "$repo_dir/version" + + # Create a tarball from the contents of the built package. + tar zpcf "$bin_dir/$1-$version-$release.tar.gz" -C "$pkg_dir/$1" . || + die "[$1]: Failed to create tarball." + + log "[$1]: Successfully created tarball." +} + pkg_build() { # Build packages and turn them into packaged tarballs. This function # also checks checksums, downloads sources and ensure all dependencies @@ -279,30 +363,7 @@ pkg_build() { die "Run '$kiss checksum ${no_checkums% }' to generate checksums." for pkg; do pkg_sources "$pkg"; done - for pkg; do - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$pkg") - - # Generate a second set of checksums to compare against the - # repositorie's checksums for the package. - pkg_checksums .checksums "$pkg" - - # Compare the checksums using 'cmp'. - cmp -s "$repo_dir/.checksums" "$repo_dir/checksums" || { - log "[$pkg]: Checksum mismatch." - - # Instead of dying above, log it to the terminal. Also define a - # variable so we *can* die after all checksum files have been - # checked. - mismatch="$mismatch$pkg " - } - - # The second set of checksums use a temporary file, we need to - # delete it. - rm -f "$repo_dir/.checksums" - done + for pkg; do pkg_verify "$pkg"; done # Die here as packages with differing checksums were found above. [ "$mismatch" ] && die "Checksum mismatch with: ${mismatch% }" @@ -339,63 +400,15 @@ pkg_build() { done log "Stripping packages..." - for pkg; do - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$pkg") - - # Package has stripping disabled, stop here. - [ -f "$repo_dir/nostrip" ] && continue - - log "[$pkg]: Stripping binaries and libraries..." - - find "$pkg_dir/$pkg" -type f | while read -r binary; do - case $(file -bi "$binary") in - application/x-sharedlib*|application/x-pie-executable*) - strip_opts=--strip-unneeded - ;; - - application/x-archive*) strip_opts=--strip-debug ;; - application/x-executable*) strip_opts=--strip-all ;; - - *) continue ;; - esac - - strip "$strip_opts" "$binary" 2>/dev/null - done - done - + for pkg; do pkg_strip "$pkg"; done log "Stripped all binaries and libraries." + log "Generating package manifests..." - - for pkg; do - # This runs in a subshell so we can avoid a 'cd -' or 'cd $OLDPWD'. - ( - cd "$pkg_dir/$pkg" - find . -type d -exec printf '%s/\n' {} + -or -print - ) | sort -r | sed -e ss.ss > "$pkg_dir/$pkg/var/db/$kiss/$pkg/manifest" - - log "[$pkg]: Generated manifest." - done - + for pkg; do pkg_manifest "$pkg"; done log "Generated all manifests." + log "Creating package tarballs..." - - for pkg; do - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$pkg") - - # Read the version information to name the package. - read -r version release < "$repo_dir/version" - - # Create a tarball from the contents of the built package. - tar zpcf "$bin_dir/$pkg-$version-$release.tar.gz" -C "$pkg_dir/$pkg" . || - die "[$pkg]: Failed to create tarball." - done - + for pkg; do pkg_tar "$pkg"; done log "Created all packages." } From fbfa692177199f7d302df098c6e9e3cdb5b65b2d Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 19:54:29 +0300 Subject: [PATCH 26/43] kiss-new: Added more comments. --- kiss-new | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/kiss-new b/kiss-new index 825cd75..146566f 100755 --- a/kiss-new +++ b/kiss-new @@ -116,7 +116,8 @@ pkg_list() { } pkg_sources() { - # Download any remote package sources. + # Download any remote package sources. The existence of local + # files is also checked. log "[$1]: Downloading sources..." # Store each downloaded source in named after the package it @@ -221,11 +222,15 @@ pkg_depends() { *" $1 "*) ;; *) + # Recurse through the dependencies of the child + # packages. Keep doing this. [ -f "$repo_dir/depends" ] && while read -r dep _; do pkg_depends "$dep" ||: done < "$repo_dir/depends" + # After child dependencies are added to the list, + # add the package which depends on them. missing_deps="$missing_deps $1 " ;; esac @@ -233,6 +238,10 @@ pkg_depends() { } pkg_verify() { + # Verify all package checksums. This is achieved by generating + # a new set of checksums and then comparing those with the old + # set. + # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data # structure. @@ -258,6 +267,9 @@ pkg_verify() { } pkg_strip() { + # Strip package binaries and libraries. This saves space on the + # system as well as on the tarballs we ship for installation. + # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data # structure. @@ -280,7 +292,9 @@ pkg_strip() { *) continue ;; esac - strip "$strip_opts" "$binary" 2>/dev/null + # Suppress errors here as some binaries and libraries may + # fail to strip. This is OK. + strip "$strip_opts" "$binary" 2>/dev/null ||: done } @@ -303,6 +317,9 @@ pkg_manifest() ( ) pkg_tar() { + # Create a tarball from the built package's files. + # This tarball also contains the package's database entry. + # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data # structure. @@ -370,6 +387,7 @@ pkg_build() { log "Verified all checksums." + log "Extracting all sources..." for pkg; do pkg_extract "$pkg"; done log "Extracted all sources." From 1c176352046c78ddfafb2eba172123ceb14aa4ff Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 21:02:23 +0300 Subject: [PATCH 27/43] kiss-new: Started working on installation. --- kiss-new | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 8 deletions(-) diff --git a/kiss-new b/kiss-new index 146566f..9b394a8 100755 --- a/kiss-new +++ b/kiss-new @@ -91,11 +91,11 @@ pkg_list() { # packages. If no arguments are passed, list all. As we # loop over '$@', if there aren't any arguments we can # just set the directory contents to the argument list. - [ "$1" ] || set -- "$KISS_ROOT/var/db/$kiss/"* + [ "$1" ] || set -- "$KISS_ROOT/var/db/kiss/"* # If the 'glob' above failed, exit early as there are no # packages installed. - [ "$1" = "$KISS_ROOT/var/db/$kiss/"\* ] && return + [ "$1" = "$KISS_ROOT/var/db/kiss/"\* ] && return # Loop over each version file and warn if one doesn't exist. # Also warn if a package is missing its version file. @@ -311,7 +311,7 @@ pkg_manifest() ( # with a trailing forward slash '/'. The list is then reversed with # directories appearing *after* their contents. find . -type d -exec printf '%s/\n' {} + -or -print | - sort -r | sed -e ss.ss > "$pkg_dir/$1/var/db/$kiss/$1/manifest" + sort -r | sed -e ss.ss > "$pkg_dir/$1/var/db/kiss/$1/manifest" log "[$1]: Generated manifest." ) @@ -329,7 +329,7 @@ pkg_tar() { read -r version release < "$repo_dir/version" # Create a tarball from the contents of the built package. - tar zpcf "$bin_dir/$1-$version-$release.tar.gz" -C "$pkg_dir/$1" . || + tar zpcf "$bin_dir/$1#$version-$release.tar.gz" -C "$pkg_dir/$1" . || die "[$1]: Failed to create tarball." log "[$1]: Successfully created tarball." @@ -400,7 +400,7 @@ pkg_build() { # Install built packages to a directory under the package name # to avod collisions with other packages. - mkdir -p "$pkg_dir/$pkg/var/db/$kiss" + mkdir -p "$pkg_dir/$pkg/var/db/kiss" # Move to the build directory and call the build script. (cd "$mak_dir/$pkg"; "$repo_dir/build" "$pkg_dir/$pkg") || @@ -408,13 +408,13 @@ pkg_build() { # Copy the repository files to the package directory. # This acts as the database entry. - cp -Rf "$repo_dir" "$pkg_dir/$pkg/var/db/$kiss/" + cp -Rf "$repo_dir" "$pkg_dir/$pkg/var/db/kiss/" log "[$pkg]: Sucessfully built package." # Create the manifest file early and make it empty. # This ensure that the manifest is added to the manifest... - : > "$pkg_dir/$pkg/var/db/$kiss/$pkg/manifest" + : > "$pkg_dir/$pkg/var/db/kiss/$pkg/manifest" done log "Stripping packages..." @@ -478,13 +478,87 @@ pkg_checksums() { done } +pkg_conflicts() { + # Check to see if a package conflicts with another. + # This function takes a path to a KISS tarball as an argument. + + # Extract manifest from the tarball and only extract files entries. + tar xf "$1" -O "./var/db/kiss/$pkg_name/manifest" | + while read -r line; do + [ "${line%%*/}" ] && printf '%s\n' "$line" >> "$cac_dir/manifest-$pid" + done ||: + + # Compare extracted manifest to all installed manifests. + # If there are matching lines (files) there is a package conflict. + for db in "$KISS_ROOT/var/db/kiss/"*; do + [ "$pkg_name" = "${db##*/}" ] && continue + + grep -Fxf "$cac_dir/manifest-$pid" "$db/manifest" 2>/dev/null && + die "Package '$pkg_name' conflicts with '${db##*/}'." + done + + # Remove this temporary file as we no longer need it. + rm -f "$cac_dir/manifest-$pid" +} + +pkg_install() { + # Install a built package tarball. + + for pkg; do + # Install can also take the full path to a tarball. + # We don't need to check the repository if this is the case. + if [ -f "$pkg" ]; then + tar_name=$pkg + + else + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + # Read the version information to name the package. + read -r version release < "$repo_dir/version" + + # Construct the name of the package tarball. + tar_name=$pkg\#$version-$release.tar.gz + + [ -f "$bin_dir/$tar_name" ] || + die "Package '$pkg' has not been built." \ + "Run '$kiss build $pkg'." + + tar_file=$bin_dir/$tar_name + fi + + # Figure out which package the tarball installs by checking for + # a database entry inside the tarball. + pkg_name=$(tar tf "$tar_file" | grep -x "\./var/db/kiss/.*/version") + pkg_name=${pkg_name%/*} + pkg_name=${pkg_name##*/} + + pkg_conflicts "$tar_file" + + tar pxf "$tar_file" -C "$tar_dir/" || + die "[$pkg]: Failed to extract tarball." + + # Create a backup of 'mv', 'mkdir' and 'find' so they aren't removed + # during package removal. This ensures that an upgrade to 'busybox' or + # your coreutils of choice doesn't break the package manager. + cp "$(command -v mv)" "$cac_dir" + cp "$(command -v mkdir)" "$cac_dir" + cp "$(command -v find)" "$cac_dir" + + log "Removing previous version of package if it exists." + pkg_remove + done +} + setup_caching() { # Setup the host machine for the package manager. Create any # directories which need to exist and set variables for easy # access to them. # Main cache directory (~/.cache/kiss/) typically. - mkdir -p "${cac_dir:=${XDG_CACHE_HOME:=$HOME/.cache}/$kiss}" || + mkdir -p "${cac_dir:=${XDG_CACHE_HOME:=$HOME/.cache}/kiss}" || die "Couldn't create cache directory ($cac_dir)." # Build directory. @@ -558,6 +632,7 @@ args() { shift [ "$1" ] || die "'kiss install' requires an argument." root_check + pkg_install "$@" ;; # Remove packages. From 6465e54d7744c0ca920ca32f2500e85f9280daee Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 23:33:42 +0300 Subject: [PATCH 28/43] kiss-new: Done? --- kiss-new | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 115 insertions(+), 9 deletions(-) diff --git a/kiss-new b/kiss-new index 9b394a8..2dd5f19 100755 --- a/kiss-new +++ b/kiss-new @@ -87,11 +87,18 @@ pkg_list() { # diectories, this just involves a simple for loop and # file read. + # Change directories to the database. This allows us to + # avoid having to basename each path. If this fails, + # set '$1' to mimic a failed glob which indicates that + # nothing is installed. + cd "$KISS_ROOT/var/db/kiss/" 2>/dev/null || + set -- "$KISS_ROOT/var/db/kiss/"\* + # Optional arguments can be passed to check for specific # packages. If no arguments are passed, list all. As we # loop over '$@', if there aren't any arguments we can # just set the directory contents to the argument list. - [ "$1" ] || set -- "$KISS_ROOT/var/db/kiss/"* + [ "$1" ] || set -- * # If the 'glob' above failed, exit early as there are no # packages installed. @@ -111,7 +118,7 @@ pkg_list() { } read -r version release < "$pkg/version" && - printf '%s\n' "${pkg%/*} $version-$release" + printf '%s\n' "$pkg $version-$release" done } @@ -310,7 +317,7 @@ pkg_manifest() ( # Find all files and directories in the package. Directories are printed # with a trailing forward slash '/'. The list is then reversed with # directories appearing *after* their contents. - find . -type d -exec printf '%s/\n' {} + -or -print | + find . -mindepth 1 -type d -exec printf '%s/\n' {} + -or -print | sort -r | sed -e ss.ss > "$pkg_dir/$1/var/db/kiss/$1/manifest" log "[$1]: Generated manifest." @@ -481,6 +488,7 @@ pkg_checksums() { pkg_conflicts() { # Check to see if a package conflicts with another. # This function takes a path to a KISS tarball as an argument. + log "Checking for package conflicts." # Extract manifest from the tarball and only extract files entries. tar xf "$1" -O "./var/db/kiss/$pkg_name/manifest" | @@ -501,6 +509,38 @@ pkg_conflicts() { rm -f "$cac_dir/manifest-$pid" } +pkg_remove() { + # Remove a package and all of its files. The '/etc' directory + # is handled differently and configuration files are *not* + # overwritten. + + # Create a backup of 'rm' and 'rmdir' so they aren't removed + # during package removal. This ensures that an upgrade to 'busybox' + # or your coreutils of choice doesn't break the package manager. + cp "$(command -v rm)" "$cac_dir" + cp "$(command -v rmdir)" "$cac_dir" + + for pkg; do + # The package is not installed, don't do anything. + pkg_list "$pkg" >/dev/null || continue + + while read -r file; do + # The file is in '/etc' skip it. This prevents the package + # manager from removing user edited config files. + [ "${file##/etc/*}" ] || continue + + if [ -d "$KISS_ROOT/$file" ]; then + "$cac_dir/rmdir" "$KISS_ROOT/$file" 2>/dev/null || continue + else + "$cac_dir/rm" -f -- "$KISS_ROOT/$file" || + log "Failed to remove '$file'." + fi + done < "$KISS_ROOT/var/db/kiss/$pkg/manifest" + + log "Successfully removed '$pkg'." + done +} + pkg_install() { # Install a built package tarball. @@ -508,7 +548,7 @@ pkg_install() { # Install can also take the full path to a tarball. # We don't need to check the repository if this is the case. if [ -f "$pkg" ]; then - tar_name=$pkg + tar_file=$pkg else # Find the package's repository files. This needs to keep @@ -530,13 +570,19 @@ pkg_install() { fi # Figure out which package the tarball installs by checking for - # a database entry inside the tarball. - pkg_name=$(tar tf "$tar_file" | grep -x "\./var/db/kiss/.*/version") - pkg_name=${pkg_name%/*} - pkg_name=${pkg_name##*/} + # a database entry inside the tarball. If no database entry exists, + # exit here as the tarball is *most likely* not a KISS package. + { + pkg_name=$(tar tf "$tar_file" | grep -x "\./var/db/kiss/.*/version") + pkg_name=${pkg_name%/*} + pkg_name=${pkg_name##*/} + } || die "'$tar_file' is not a valid KISS package." pkg_conflicts "$tar_file" + # Extract the tarball early to catch any errors before installation + # begins. The package manager uninstalls the previous package during + # an upgrade so any errors need to be caught ASAP. tar pxf "$tar_file" -C "$tar_dir/" || die "[$pkg]: Failed to extract tarball." @@ -548,7 +594,57 @@ pkg_install() { cp "$(command -v find)" "$cac_dir" log "Removing previous version of package if it exists." - pkg_remove + pkg_remove "$pkg_name" + + # Installation works by unpacking the tarball to a specified location, + # manually running 'mkdir' to create each directory and finally, using + # 'mv' to move each file. + cd "$tar_dir" + + # Create all of the package's directories. + # Optimization: Only find the deepest directories. + "$cac_dir/find" . -type d -links -3 -prune | while read -r dir; do + "$cac_dir/mkdir" -p "$KISS_ROOT/${dir#./}" + done + + # Move all package files to '$KISS_ROOT'. + "$cac_dir/find" ./ -mindepth 1 -not -type d | while read -r file; do + rpath=${file#.} + + # Don't overwrite existing '/etc' files. + [ -z "${rpath##/etc/*}" ] && + [ -f "$KISS_ROOT/${rpath%/*}/${file##*/}" ] && + return + + "$cac_dir/mv" "$file" "$KISS_ROOT/${rpath%/*}" + done + + # Run the post install script and suppress errors. If it exists, + # it will run, else nothing will happen. + "$KISS_ROOT/var/db/kiss/$pkg_name/post-install" 2>/dev/null ||: + + log "Successfully installed '$pkg_name'." + done +} + +pkg_updates() { + # Check all installed packages for updates. So long as the installed + # version and the version in the repositories differ, it's considered + # an update. + for pkg in "$KISS_ROOT/var/db/kiss/"*; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "${pkg##*/}") + + # Read version and release information from the installed packages + # and repository. + read -r db_ver db_rel < "$pkg/version" + read -r re_ver re_rel < "$repo_dir/version" + + # Compare installed packages to repository packages. + [ "$db_ver-$db_rel" != "$re_ver-$re_rel" ] && + printf '%s\n' "${pkg##*/} $re_ver-$re_rel" done } @@ -588,6 +684,10 @@ pkg_clean() { # Remove temporary directories. rm -rf -- "$mak_dir" "$pkg_dir" "$tar_dir" + + # Remove cached commands. + rm -f -- "$cac_dir/find" "$cac_dir/mv" "$cac_dir/mkdir" \ + "$cac_dir/rm" "$cac_dir/rmdir" } root_check() { @@ -640,6 +740,7 @@ args() { shift [ "$1" ] || die "'kiss remove' requires an argument." root_check + pkg_remove "$@" ;; # List installed packages. @@ -648,6 +749,11 @@ args() { pkg_list "$@" ;; + # Upgrade packages. + u*) + pkg_updates + ;; + # Print version and exit. v*) log "$kiss 0.1.10" From 399bf0332971436f9602b689257b1b190ac65e58 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 23:36:23 +0300 Subject: [PATCH 29/43] kiss-new: Fix error. --- kiss-new | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kiss-new b/kiss-new index 2dd5f19..d88b035 100755 --- a/kiss-new +++ b/kiss-new @@ -102,7 +102,7 @@ pkg_list() { # If the 'glob' above failed, exit early as there are no # packages installed. - [ "$1" = "$KISS_ROOT/var/db/kiss/"\* ] && return + [ "$1" = "$KISS_ROOT/var/db/kiss/"\* ] && return 1 # Loop over each version file and warn if one doesn't exist. # Also warn if a package is missing its version file. From f30053013bb20104bbc1942d1c68a787482f3eaf Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sat, 29 Jun 2019 23:38:35 +0300 Subject: [PATCH 30/43] kiss: update --- kiss | 858 ++++++++++++++++++++++++++++++++++++++++++------------- kiss-new | 796 --------------------------------------------------- 2 files changed, 660 insertions(+), 994 deletions(-) delete mode 100755 kiss-new diff --git a/kiss b/kiss index 343d5db..d88b035 100755 --- a/kiss +++ b/kiss @@ -1,6 +1,25 @@ -#!/bin/sh +#!/bin/sh -e # -# kiss - package manager for kiss linux. +# This is a simple package manager written in POSIX 'sh' for +# KISS Linux utlizing the core unix utilites where needed. +# +# The script runs with 'set -e' enabled. It will exit on any +# non-zero return code. This ensures that no function continues +# if it fails at any point. +# +# Keep in mind that this involves extra code in the case where +# an error is optional or required. +# +# Where possible the package manager should "error first". +# Check things first, die is necessary and continue if all is well. +# +# The code below conforms to shellcheck's rules. However, some +# lint errors *are* disabled as they relate to unexpected +# behavior (which we do expect). +# +# KISS is available under the MIT license. +# +# - Dylan Araps. die() { # Print a message and exit with '1' (error). @@ -14,32 +33,41 @@ log() { printf '\033[32m=>\033[m %s\n' "$@" } -source_type() { - # Figure out what kind of source we are dealing with. - # This removes the need to repeat these same tests - # in each function. - [ -z "$1" ] && return 1 # No file. - [ -f "$1" ] && return 2 # Local file. - [ -f "$src_dir/${1##*/}" ] && return 3 # Cached downloaded file. - [ -z "${1##git:*}" ] && return 4 # Git repository. - [ -z "${1##*://*}" ] && return 5 # Remote file. -} +pkg_lint() { + # Check that each mandatory file in the package entry exists. + log "[$1]: Checking repository files..." -pkg_clean() { - # Clean up on exit or error. This removes everything related - # to the build. - rm -rf -- "$mak_dir" "$pkg_dir" "$tar_dir" \ - "$cac_dir/manifest-$$" "$cac_dir/checksums-$$" \ - "$cac_dir/mv" "$cac_dir/mkdir" "$cac_dir/find" + pkg_location=$(pkg_search "$1") + + cd "$pkg_location" || die "'$pkg_location' not accessible" + + [ -f sources ] || die "Sources file not found." + [ -x build ] || die "Build file not found or not executable." + [ -s licenses ] || die "License file not found or empty." + [ -s version ] || die "Version file not found or empty." + + # Ensure that the release field in the version file is set + # to something. + read -r _ rel < version + [ "$rel" ] || die "Release field not found in version file." + + # Unset this variable so it isn't used again on a failed + # source. There's no 'local' keyword in POSIX sh. + rel= } pkg_search() { # Figure out which repository a package belongs to by # searching for directories matching the package name - # in $KISS_PATH. + # in $KISS_PATH/*. + [ "$KISS_PATH" ] || \ + die "\$KISS_PATH needs to be set." \ + "Example: KISS_PATH=/packages/core:/packages/extra:/packages/xorg" \ + "Repositories will be searched in the configured order." \ + "The variable should work just like \$PATH." # Disable globbing with 'set -f' to ensure that the unquoted - # variable doesn't expand to anything nasty. + # variable doesn't expand into anything nasty. # shellcheck disable=2086,2046 { set -f @@ -47,99 +75,219 @@ pkg_search() { set +f } + # A package may also not be found due to a repository not being + # readable by the current user. Either way, we need to die here. [ -z "$2" ] && die "Package '$1' not in any repository." printf '%s\n' "$2" } -pkg_setup() { - # Check that each mandatory file in the package entry exists. - pkg_location=$(pkg_search "$1") +pkg_list() { + # List installed packages. As the format is files and + # diectories, this just involves a simple for loop and + # file read. - cd "$pkg_location" || die "'$pkg_location' not accessible" - [ -f sources ] || die "Sources file not found." - [ -x build ] || die "Build file not found or not executable." - [ -f licenses ] || die "License file not found or empty." + # Change directories to the database. This allows us to + # avoid having to basename each path. If this fails, + # set '$1' to mimic a failed glob which indicates that + # nothing is installed. + cd "$KISS_ROOT/var/db/kiss/" 2>/dev/null || + set -- "$KISS_ROOT/var/db/kiss/"\* - read -r pkg_ver pkg_rel < version || die "Version file not found." - pkg_name=$1 - pkg_tar=$name\#$ver-$rel.tar.gz -} + # Optional arguments can be passed to check for specific + # packages. If no arguments are passed, list all. As we + # loop over '$@', if there aren't any arguments we can + # just set the directory contents to the argument list. + [ "$1" ] || set -- * -pkg_depends() { - [ -f depends ] && while read -r dep opt; do - pkg_list "$dep" || { - [ "$1" = install ] && [ "$opt" = make ] && continue + # If the 'glob' above failed, exit early as there are no + # packages installed. + [ "$1" = "$KISS_ROOT/var/db/kiss/"\* ] && return 1 - case $missing in - *" $dep,"*) ;; - *) missing="$missing $dep," - pkg_setup "$dep" - pkg_depends ;; - esac + # Loop over each version file and warn if one doesn't exist. + # Also warn if a package is missing its version file. + for pkg; do + [ -d "$pkg" ] || { + log "Package '$pkg' is not installed." + return 1 } - done < depends + + [ -f "$pkg/version" ] || { + log "Warning: Package '$pkg' has no version file." + return + } + + read -r version release < "$pkg/version" && + printf '%s\n' "$pkg $version-$release" + done } pkg_sources() { - src_dir=$cac_dir/sources/$name - mkdir -p "$src_dir" + # Download any remote package sources. The existence of local + # files is also checked. + log "[$1]: Downloading sources..." + + # Store each downloaded source in named after the package it + # belongs to. This avoid conflicts between two packages having a + # source of the same name. + mkdir -p "$src_dir/$1" && cd "$src_dir/$1" + + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$1") while read -r src _; do - case $(source_type "$src"; echo $?) in - 4) git clone "${src##git:}" "$mak_dir" ;; - 5) wget -P "$src_dir" "$src" || die "Failed to download $src." ;; - 0|1) die "Source file '$src' not found." ;; + case $src in + # Git repository. + git:*) + git clone "${src##git:}" "$mak_dir" + ;; + + # Remote source. + *://*) + [ -f "${src##*/}" ] && { + log "[$1]: Found cached source '${src##*/}'." + continue + } + + wget "$src" || { + rm -f "${src##*/}" + die "[$1]: Failed to download $src." + } + ;; + + # Local files (Any source that is non-remote is assumed to be local). + *) + [ -f "$repo_dir/$src" ] || + die "[$1]: No local file '$src'." + + log "[$1]: Found local file '$src'." + ;; esac - done < sources -} - -pkg_checksum() { - while read -r src _; do - case $(source_type "$src"; echo $?) in - 2) src_path=$src ;; - 3) src_path=$src_dir/${src##*/} ;; - 4) continue - esac - - (cd "${src_path%/*}" >/dev/null; sha256sum "${src##*/}") || - die "Failed to generate checksums." - done < sources > "${1-checksums}" -} - -pkg_verify() { - pkg_checksum "$cac_dir/checksums-$$" - - cmp -s "$cac_dir/checksums-$$" checksums || - die "Checksum mismatch, run '$kiss checksum $name'." + done < "$repo_dir/sources" } pkg_extract() { + # Extract all source archives to the build diectory and copy over + # any local repository files. + log "[$1]: Extracting sources..." + + # Store each downloaded source in named after the package it + # belongs to. This avoid conflicts between two packages having a + # source of the same name. + mkdir -p "$mak_dir/$1" && cd "$mak_dir/$1" + + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$1") + while read -r src dest; do - [ "$dest" ] && mkdir -p "$mak_dir/$dest" + mkdir -p "./$dest" - case $(source_type "$src"; echo $?)-$src in - 2-*) cp -f "$src" "$mak_dir/$dest" ;; + case $src in + # Do nothing as git repository was downloaded to the build + # diectory directly. + git:*) ;; - 3-*.tar*|3-*.tgz) - tar xf "$src_dir/${src##*/}" -C "$mak_dir/$dest" \ - --strip-components 1 || die "Couldn't extract ${src##*/}" ;; + # Only 'tar' archives are currently supported for extaction. + # Any other filetypes are simply copied to '$mak_dir' which + # allows you to extract them manually. + *://*.tar*|*://*.tgz) + tar xf "$src_dir/$1/${src##*/}" -C "./$dest" \ + --strip-components 1 \ + || die "[$1]: Couldn't extract ${src##*/}." + ;; - [01]-*) die "${src##*/} not found." + # Local files (Any source that is non-remote is assumed to be local). + *) + [ -f "$repo_dir/$src" ] || die "[$1]: Local file $src not found." + cp -f "$repo_dir/$src" "./$dest" + ;; esac - done < sources + done < "$repo_dir/sources" } -pkg_build() { - (cd "$mak_dir"; "$OLDPWD/build" "$pkg_dir") || die "Build failed." - cp -Rf "$rep_dir/$name" "$pkg_db" - log "Sucessfully built $pkg." 2> "$pkg_db/$name/manifest" +pkg_depends() { + # Resolve all dependencies and install them in the right order. + + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$1") + + # This does a depth-first search. The deepest dependencies are + # listed first and then the parents in reverse order. + if pkg_list "$1" >/dev/null; then + # If a package is already installed but 'pkg_depends' was + # given an argument, add it to the list anyway. + [ "$2" ] && missing_deps="$missing_deps $1 " + else + case $missing_deps in + # Dependency is already in list, skip it. + *" $1 "*) ;; + + *) + # Recurse through the dependencies of the child + # packages. Keep doing this. + [ -f "$repo_dir/depends" ] && + while read -r dep _; do + pkg_depends "$dep" ||: + done < "$repo_dir/depends" + + # After child dependencies are added to the list, + # add the package which depends on them. + missing_deps="$missing_deps $1 " + ;; + esac + fi +} + +pkg_verify() { + # Verify all package checksums. This is achieved by generating + # a new set of checksums and then comparing those with the old + # set. + + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$1") + + # Generate a second set of checksums to compare against the + # repositorie's checksums for the package. + pkg_checksums .checksums "$1" + + # Compare the checksums using 'cmp'. + cmp -s "$repo_dir/.checksums" "$repo_dir/checksums" || { + log "[$1]: Checksum mismatch." + + # Instead of dying above, log it to the terminal. Also define a + # variable so we *can* die after all checksum files have been + # checked. + mismatch="$mismatch$1 " + } + + # The second set of checksums use a temporary file, we need to + # delete it. + rm -f "$repo_dir/.checksums" } pkg_strip() { - log "Stripping unneeded symbols from binaries and libraries." + # Strip package binaries and libraries. This saves space on the + # system as well as on the tarballs we ship for installation. - find "$pkg_dir" -type f | while read -r binary; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$1") + + # Package has stripping disabled, stop here. + [ -f "$repo_dir/nostrip" ] && return + + log "[$1]: Stripping binaries and libraries..." + + find "$pkg_dir/$1" -type f | while read -r binary; do case $(file -bi "$binary") in application/x-sharedlib*|application/x-pie-executable*) strip_opts=--strip-unneeded @@ -151,183 +299,497 @@ pkg_strip() { *) continue ;; esac - strip "$strip_opts" "$binary" 2>/dev/null + # Suppress errors here as some binaries and libraries may + # fail to strip. This is OK. + strip "$strip_opts" "$binary" 2>/dev/null ||: done } -pkg_manifest() { - # Store the file and directory list of the package. - # Directories have a trailing '/' and the list is sorted in reverse. - (cd "$pkg_dir" && find ./* -type d -exec printf '%s/\n' {} + -or -print) | - sort -r | sed -e ss.ss > "$pkg_db/$name/manifest" -} +pkg_manifest() ( + # Generate the package's manifest file. This is a list of each file + # and directory inside the package. The file is used when uninstalling + # packages, checking for package conflicts and for general debugging. + # + # This funcion runs as a subshell to avoid having to 'cd' back to the + # prior directory before being able to continue. + cd "$pkg_dir/$1" + + # Find all files and directories in the package. Directories are printed + # with a trailing forward slash '/'. The list is then reversed with + # directories appearing *after* their contents. + find . -mindepth 1 -type d -exec printf '%s/\n' {} + -or -print | + sort -r | sed -e ss.ss > "$pkg_dir/$1/var/db/kiss/$1/manifest" + + log "[$1]: Generated manifest." +) pkg_tar() { - tar zpcf "$bin_dir/$pkg" -C "$pkg_dir" . || die "Failed to create package." - log "Use '$kiss install $name' to install the package." + # Create a tarball from the built package's files. + # This tarball also contains the package's database entry. + + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$1") + + # Read the version information to name the package. + read -r version release < "$repo_dir/version" + + # Create a tarball from the contents of the built package. + tar zpcf "$bin_dir/$1#$version-$release.tar.gz" -C "$pkg_dir/$1" . || + die "[$1]: Failed to create tarball." + + log "[$1]: Successfully created tarball." +} + +pkg_build() { + # Build packages and turn them into packaged tarballs. This function + # also checks checksums, downloads sources and ensure all dependencies + # are installed. + + # Resolve dependencies and generate a list. + # Send 'force' to 'pkg_depends' to always include the explicitly + # requested packages. + log "Resolving dependencies..." + for pkg; do pkg_depends "$pkg" force; done + + # Disable globbing with 'set -f' to ensure that the unquoted + # variable doesn't expand into anything nasty. + # shellcheck disable=2086,2046 + { + # Set the resolved dependency list as the function's arguments. + set -f + set -- $missing_deps + set +f + } + log "Installing: $*." + + for pkg; do pkg_lint "$pkg"; done + for pkg; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + # Ensure that checksums exist prior to building the package. + [ -f "$repo_dir/checksums" ] || { + log "[$pkg]: Checksums are missing." + + # Instead of dying above, log it to the terminal. Also define a + # variable so we *can* die after all checksum files have been + # checked. + no_checkums="$no_checkums$pkg " + } + done + + # Die here as packages without checksums were found above. + [ "$no_checkums" ] && + die "Run '$kiss checksum ${no_checkums% }' to generate checksums." + + for pkg; do pkg_sources "$pkg"; done + for pkg; do pkg_verify "$pkg"; done + + # Die here as packages with differing checksums were found above. + [ "$mismatch" ] && die "Checksum mismatch with: ${mismatch% }" + + log "Verified all checksums." + + log "Extracting all sources..." + for pkg; do pkg_extract "$pkg"; done + log "Extracted all sources." + + log "Building packages..." + for pkg; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + # Install built packages to a directory under the package name + # to avod collisions with other packages. + mkdir -p "$pkg_dir/$pkg/var/db/kiss" + + # Move to the build directory and call the build script. + (cd "$mak_dir/$pkg"; "$repo_dir/build" "$pkg_dir/$pkg") || + die "[$pkg]: Build failed." + + # Copy the repository files to the package directory. + # This acts as the database entry. + cp -Rf "$repo_dir" "$pkg_dir/$pkg/var/db/kiss/" + + log "[$pkg]: Sucessfully built package." + + # Create the manifest file early and make it empty. + # This ensure that the manifest is added to the manifest... + : > "$pkg_dir/$pkg/var/db/kiss/$pkg/manifest" + done + + log "Stripping packages..." + for pkg; do pkg_strip "$pkg"; done + log "Stripped all binaries and libraries." + + log "Generating package manifests..." + for pkg; do pkg_manifest "$pkg"; done + log "Generated all manifests." + + log "Creating package tarballs..." + for pkg; do pkg_tar "$pkg"; done + log "Created all packages." +} + +pkg_checksums() { + # Generate checksums for packages. + # This also downloads any remote sources. + checksum_file=$1 + shift + + for pkg; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + while read -r src _; do + case $src in + # Git repository. + # Skip checksums on git repositories. + git:*) ;; + + *) + # File is local to the package and is stored in the + # repository. + [ -f "$repo_dir/$src" ] && + src_path=$repo_dir/${src%/*} + + # File is remote and was downloaded. + [ -f "$src_dir/$pkg/${src##*/}" ] && + src_path=$src_dir/$pkg + + # Die here if source for some reason, doesn't exist. + [ "$src_path" ] || + die "[$pkg]: Couldn't find source '$src'." + + # An easy way to get 'sha256sum' to print with the basenames + # of files is to 'cd' to the file's directory beforehand. + (cd "$src_path" && sha256sum "${src##*/}") || + die "[$pkg]: Failed to generate checksums." + + # Unset this variable so it isn't used again on a failed + # source. There's no 'local' keyword in POSIX sh. + src_path= + ;; + esac + done < "$repo_dir/sources" > "$repo_dir/$checksum_file" + + log "[$pkg]: Generated/Verified checksums." + done } pkg_conflicts() { + # Check to see if a package conflicts with another. + # This function takes a path to a KISS tarball as an argument. log "Checking for package conflicts." - # Extract manifest from tarball and strip directories. - tar xf "$bin_dir/$pkg" -O "./var/db/$kiss/$name/manifest" | + # Extract manifest from the tarball and only extract files entries. + tar xf "$1" -O "./var/db/kiss/$pkg_name/manifest" | while read -r line; do - [ "${line%%*/}" ] && printf '%s\n' "$line" >> "$cac_dir/manifest-$$" - done + [ "${line%%*/}" ] && printf '%s\n' "$line" >> "$cac_dir/manifest-$pid" + done ||: # Compare extracted manifest to all installed manifests. - # If there are matching lines (files) there's a package - # conflict. - for db in "$sys_db"/*; do - [ "$name" = "${db##*/}" ] && continue + # If there are matching lines (files) there is a package conflict. + for db in "$KISS_ROOT/var/db/kiss/"*; do + [ "$pkg_name" = "${db##*/}" ] && continue - grep -Fxf "$cac_dir/manifest-$$" "$db/manifest" 2>/dev/null && - die "Package '$name' conflicts with '${db##*/}'." + grep -Fxf "$cac_dir/manifest-$pid" "$db/manifest" 2>/dev/null && + die "Package '$pkg_name' conflicts with '${db##*/}'." + done + + # Remove this temporary file as we no longer need it. + rm -f "$cac_dir/manifest-$pid" +} + +pkg_remove() { + # Remove a package and all of its files. The '/etc' directory + # is handled differently and configuration files are *not* + # overwritten. + + # Create a backup of 'rm' and 'rmdir' so they aren't removed + # during package removal. This ensures that an upgrade to 'busybox' + # or your coreutils of choice doesn't break the package manager. + cp "$(command -v rm)" "$cac_dir" + cp "$(command -v rmdir)" "$cac_dir" + + for pkg; do + # The package is not installed, don't do anything. + pkg_list "$pkg" >/dev/null || continue + + while read -r file; do + # The file is in '/etc' skip it. This prevents the package + # manager from removing user edited config files. + [ "${file##/etc/*}" ] || continue + + if [ -d "$KISS_ROOT/$file" ]; then + "$cac_dir/rmdir" "$KISS_ROOT/$file" 2>/dev/null || continue + else + "$cac_dir/rm" -f -- "$KISS_ROOT/$file" || + log "Failed to remove '$file'." + fi + done < "$KISS_ROOT/var/db/kiss/$pkg/manifest" + + log "Successfully removed '$pkg'." done } pkg_install() { - [ -f "$bin_dir/$pkg" ] || args b "$name" + # Install a built package tarball. - pkg_conflicts - tar pxf "$bin_dir/$pkg" -C "$tar_dir/" || die "Failed to extract tarball." + for pkg; do + # Install can also take the full path to a tarball. + # We don't need to check the repository if this is the case. + if [ -f "$pkg" ]; then + tar_file=$pkg - # Create a backup of 'mv', 'mkdir' and 'find' so they aren't removed - # during package removal. - cp "$(command -v mv)" "$cac_dir" - cp "$(command -v mkdir)" "$cac_dir" - cp "$(command -v find)" "$cac_dir" - - log "Removing previous version of package if it exists." - pkg_remove - - cd "$tar_dir" || die "Aborting due to tar error." - - # Optimization: Only find the deepest directories. - "$cac_dir/find" . -type d -links -3 -prune | while read -r dir; do - "$cac_dir/mkdir" -p "$sys_dir/${dir#./}" - done - - "$cac_dir/find" ./ -mindepth 1 -not -type d | while read -r file; do - rpath=${file#.} - - [ -z "${rpath##/etc/*}" ] && [ -f "$sys_dir${rpath%/*}/${file##*/}" ] && - return - - "$cac_dir/mv" "$file" "$sys_dir${rpath%/*}" - done - - "$sys_db/$name/post-install" 2>/dev/null - - log "Installed ${pkg%.tar.gz}" -} - -pkg_remove() { - pkg_list "${1:-${name-null}}" || return 1 - - # Create a backup of 'rm' and 'rmdir' so they aren't - # removed during package removal. - cp "$(command -v rm)" "$cac_dir" - cp "$(command -v rmdir)" "$cac_dir" - - while read -r file; do - [ "${file##/etc/*}" ] || continue - - if [ -d "$sys_dir$file" ]; then - "$cac_dir/rmdir" "$sys_dir$file" 2>/dev/null || continue else - "$cac_dir/rm" -f -- "$sys_dir$file" || log "Failed to remove $file." + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + # Read the version information to name the package. + read -r version release < "$repo_dir/version" + + # Construct the name of the package tarball. + tar_name=$pkg\#$version-$release.tar.gz + + [ -f "$bin_dir/$tar_name" ] || + die "Package '$pkg' has not been built." \ + "Run '$kiss build $pkg'." + + tar_file=$bin_dir/$tar_name fi - done < "$sys_db/${1:-$name}/manifest" - # Use the backup of 'rm' to remove 'rmdir' and itself. - "$cac_dir/rm" "$cac_dir/rmdir" "$cac_dir/rm" + # Figure out which package the tarball installs by checking for + # a database entry inside the tarball. If no database entry exists, + # exit here as the tarball is *most likely* not a KISS package. + { + pkg_name=$(tar tf "$tar_file" | grep -x "\./var/db/kiss/.*/version") + pkg_name=${pkg_name%/*} + pkg_name=${pkg_name##*/} + } || die "'$tar_file' is not a valid KISS package." - log "Removed ${1:-$name}." + pkg_conflicts "$tar_file" + + # Extract the tarball early to catch any errors before installation + # begins. The package manager uninstalls the previous package during + # an upgrade so any errors need to be caught ASAP. + tar pxf "$tar_file" -C "$tar_dir/" || + die "[$pkg]: Failed to extract tarball." + + # Create a backup of 'mv', 'mkdir' and 'find' so they aren't removed + # during package removal. This ensures that an upgrade to 'busybox' or + # your coreutils of choice doesn't break the package manager. + cp "$(command -v mv)" "$cac_dir" + cp "$(command -v mkdir)" "$cac_dir" + cp "$(command -v find)" "$cac_dir" + + log "Removing previous version of package if it exists." + pkg_remove "$pkg_name" + + # Installation works by unpacking the tarball to a specified location, + # manually running 'mkdir' to create each directory and finally, using + # 'mv' to move each file. + cd "$tar_dir" + + # Create all of the package's directories. + # Optimization: Only find the deepest directories. + "$cac_dir/find" . -type d -links -3 -prune | while read -r dir; do + "$cac_dir/mkdir" -p "$KISS_ROOT/${dir#./}" + done + + # Move all package files to '$KISS_ROOT'. + "$cac_dir/find" ./ -mindepth 1 -not -type d | while read -r file; do + rpath=${file#.} + + # Don't overwrite existing '/etc' files. + [ -z "${rpath##/etc/*}" ] && + [ -f "$KISS_ROOT/${rpath%/*}/${file##*/}" ] && + return + + "$cac_dir/mv" "$file" "$KISS_ROOT/${rpath%/*}" + done + + # Run the post install script and suppress errors. If it exists, + # it will run, else nothing will happen. + "$KISS_ROOT/var/db/kiss/$pkg_name/post-install" 2>/dev/null ||: + + log "Successfully installed '$pkg_name'." + done } pkg_updates() { - for item in "$sys_db/"*; do - pkg_search "${item##*/}" + # Check all installed packages for updates. So long as the installed + # version and the version in the repositories differ, it's considered + # an update. + for pkg in "$KISS_ROOT/var/db/kiss/"*; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "${pkg##*/}") - read -r db_ver db_rel < "$item/version" - read -r re_ver re_rel < "$rep_dir/${item##*/}/version" + # Read version and release information from the installed packages + # and repository. + read -r db_ver db_rel < "$pkg/version" + read -r re_ver re_rel < "$repo_dir/version" + # Compare installed packages to repository packages. [ "$db_ver-$db_rel" != "$re_ver-$re_rel" ] && - printf '%s\n' "${item##*/} $re_ver-$re_rel" + printf '%s\n' "${pkg##*/} $re_ver-$re_rel" done } -pkg_list() { - [ "$1" ] && { [ -d "$sys_db/$1" ]; return "$?"; } +setup_caching() { + # Setup the host machine for the package manager. Create any + # directories which need to exist and set variables for easy + # access to them. - for item in "$sys_db/"*; do - read -r version release 2>/dev/null < "$item/version" && - printf '%s\n' "${item##*/} $version-$release" - done + # Main cache directory (~/.cache/kiss/) typically. + mkdir -p "${cac_dir:=${XDG_CACHE_HOME:=$HOME/.cache}/kiss}" || + die "Couldn't create cache directory ($cac_dir)." + + # Build directory. + mkdir -p "${mak_dir:=$cac_dir/build-$pid}" || + die "Couldn't create build directory ($mak_dir)." + + # Package directory. + mkdir -p "${pkg_dir:=$cac_dir/pkg-$pid}" || + die "Couldn't create package directory ($pkg_dir)." + + # Tar directory. + mkdir -p "${tar_dir:=$cac_dir/extract-$pid}" || + die "Couldn't create tar directory ($tar_dir)." + + # Source directory. + mkdir -p "${src_dir:=$cac_dir/sources}" || + die "Couldn't create source directory ($src_dir)." + + # Binary directory. + mkdir -p "${bin_dir:=$cac_dir/bin}" || + die "Couldn't create binary directory ($bin_dir)." +} + +pkg_clean() { + # Clean up on exit or error. This removes everything related + # to the build. + + # Remove temporary directories. + rm -rf -- "$mak_dir" "$pkg_dir" "$tar_dir" + + # Remove cached commands. + rm -f -- "$cac_dir/find" "$cac_dir/mv" "$cac_dir/mkdir" \ + "$cac_dir/rm" "$cac_dir/rmdir" +} + +root_check() { + # Ensure that the user has write permissions to '$KISS_ROOT'. + # When this variable is empty, a value of '/' is assumed. + [ -w "$KISS_ROOT/" ] || \ + die "No write permissions to '${KISS_ROOT:-/}'." \ + "You may need to run '$kiss' as root." } args() { - [ -w "$sys_dir/" ] || case $1 in - i*|r*) die "No write permissions to \$KISS_ROOT." - esac + # Parse script arguments manually. POSIX 'sh' has no 'getopts' + # or equivalent built in. This is rather easy to do in our case + # since the first argument is always an "action" and the arguments + # that follow are all package names. - case $1 in b*|c*|i*) pkg_setup "${2-null}"; esac + # Actions can be abbreviated to their first letter. This saves + # keystrokes once you memorize themand it also has the side-effect + # of "correcting" spelling mistakes assuming the first letter is + # right. case $1 in - b*) [ -f checksums ] || - die "Checksums missing, run '$kiss checksum $name'" + # Build the list of packages. + b*) + shift + [ "$1" ] || die "'kiss build' requires an argument." + pkg_build "$@" + ;; - pkg_depends + # Generate checksums for packages. + c*) + shift + [ "$1" ] || die "'kiss checksum' requires an argument." - [ -n "$missing" ] && die "Missing dependencies:${missing%,}" + for pkg; do pkg_lint "$pkg"; done + for pkg; do pkg_sources "$pkg"; done - pkg_sources - pkg_verify - pkg_extract - pkg_build + pkg_checksums checksums "$@" + ;; - [ -f nostrip ] || pkg_strip + # Install packages. + i*) + shift + [ "$1" ] || die "'kiss install' requires an argument." + root_check + pkg_install "$@" + ;; - pkg_manifest - pkg_tar ;; + # Remove packages. + r*) + shift + [ "$1" ] || die "'kiss remove' requires an argument." + root_check + pkg_remove "$@" + ;; - c*) pkg_sources - pkg_checksum - log "Generated checksums." ;; + # List installed packages. + l*) + shift + pkg_list "$@" + ;; - i*) pkg_depends install - pkg_install ;; + # Upgrade packages. + u*) + pkg_updates + ;; - l*) pkg_list "$2" ;; - r*) pkg_remove "${2-null}" || die "Package '${2-null}' not installed." ;; - u*) pkg_updates ;; - v*) log "$kiss 0.1.10" ;; + # Print version and exit. + v*) + log "$kiss 0.1.10" + ;; - *) log "$kiss [b|c|i|l|r|u] [pkg]" \ + # Catch all invalid arguments as well as + # any help related flags (-h, --help, help). + *) + log "$kiss [b|c|i|l|r|u] [pkg]" \ "build: Build a package." \ "checksum: Generate checksums." \ "install: Install a package (Runs build if needed)." \ "list: List packages." \ "remove: Remove a package." \ "update: Check for updates." + ;; esac } main() { - trap pkg_clean EXIT INT + # Store the script name in a variable and use it everywhere + # in place of 'kiss'. This allows the script name to be changed + # easily. kiss=${0##*/} - sys_db=${sys_dir:=$KISS_ROOT}/var/db/$kiss - [ -z "$KISS_PATH" ] && die "Set \$KISS_PATH to a repository location." + # The PID of the current shell process is used to isolate directories + # to each specific KISS instance. This allows multiple package manager + # instances to be run at once. Store the value in another variable so + # that it doesn't change beneath us. + pid=$$ - mkdir -p "${cac_dir:=${XDG_CACHE_HOME:=$HOME/.cache}/$kiss}" \ - "${mak_dir:=$cac_dir/build-$$}" \ - "${bin_dir:=$cac_dir/bin}" \ - "${tar_dir:=$cac_dir/extract-$$}" \ - "${pkg_db:=${pkg_dir:=$cac_dir/pkg-$$}/var/db/$kiss}" || - die "Couldn't create directories." + # Catch errors and ensure that build files and directories are cleaned + # up before we die. This occurs on 'Ctrl+C' as well as sucess and error. + trap pkg_clean EXIT INT + setup_caching args "$@" } diff --git a/kiss-new b/kiss-new deleted file mode 100755 index d88b035..0000000 --- a/kiss-new +++ /dev/null @@ -1,796 +0,0 @@ -#!/bin/sh -e -# -# This is a simple package manager written in POSIX 'sh' for -# KISS Linux utlizing the core unix utilites where needed. -# -# The script runs with 'set -e' enabled. It will exit on any -# non-zero return code. This ensures that no function continues -# if it fails at any point. -# -# Keep in mind that this involves extra code in the case where -# an error is optional or required. -# -# Where possible the package manager should "error first". -# Check things first, die is necessary and continue if all is well. -# -# The code below conforms to shellcheck's rules. However, some -# lint errors *are* disabled as they relate to unexpected -# behavior (which we do expect). -# -# KISS is available under the MIT license. -# -# - Dylan Araps. - -die() { - # Print a message and exit with '1' (error). - printf '\033[31m!>\033[m %s\n' "$@" >&2 - exit 1 -} - -log() { - # Print a message with a colorful arrow to distinguish - # from other output. - printf '\033[32m=>\033[m %s\n' "$@" -} - -pkg_lint() { - # Check that each mandatory file in the package entry exists. - log "[$1]: Checking repository files..." - - pkg_location=$(pkg_search "$1") - - cd "$pkg_location" || die "'$pkg_location' not accessible" - - [ -f sources ] || die "Sources file not found." - [ -x build ] || die "Build file not found or not executable." - [ -s licenses ] || die "License file not found or empty." - [ -s version ] || die "Version file not found or empty." - - # Ensure that the release field in the version file is set - # to something. - read -r _ rel < version - [ "$rel" ] || die "Release field not found in version file." - - # Unset this variable so it isn't used again on a failed - # source. There's no 'local' keyword in POSIX sh. - rel= -} - -pkg_search() { - # Figure out which repository a package belongs to by - # searching for directories matching the package name - # in $KISS_PATH/*. - [ "$KISS_PATH" ] || \ - die "\$KISS_PATH needs to be set." \ - "Example: KISS_PATH=/packages/core:/packages/extra:/packages/xorg" \ - "Repositories will be searched in the configured order." \ - "The variable should work just like \$PATH." - - # Disable globbing with 'set -f' to ensure that the unquoted - # variable doesn't expand into anything nasty. - # shellcheck disable=2086,2046 - { - set -f - set -- "$1" $(IFS=:; find $KISS_PATH -maxdepth 1 -name "$1") - set +f - } - - # A package may also not be found due to a repository not being - # readable by the current user. Either way, we need to die here. - [ -z "$2" ] && die "Package '$1' not in any repository." - - printf '%s\n' "$2" -} - -pkg_list() { - # List installed packages. As the format is files and - # diectories, this just involves a simple for loop and - # file read. - - # Change directories to the database. This allows us to - # avoid having to basename each path. If this fails, - # set '$1' to mimic a failed glob which indicates that - # nothing is installed. - cd "$KISS_ROOT/var/db/kiss/" 2>/dev/null || - set -- "$KISS_ROOT/var/db/kiss/"\* - - # Optional arguments can be passed to check for specific - # packages. If no arguments are passed, list all. As we - # loop over '$@', if there aren't any arguments we can - # just set the directory contents to the argument list. - [ "$1" ] || set -- * - - # If the 'glob' above failed, exit early as there are no - # packages installed. - [ "$1" = "$KISS_ROOT/var/db/kiss/"\* ] && return 1 - - # Loop over each version file and warn if one doesn't exist. - # Also warn if a package is missing its version file. - for pkg; do - [ -d "$pkg" ] || { - log "Package '$pkg' is not installed." - return 1 - } - - [ -f "$pkg/version" ] || { - log "Warning: Package '$pkg' has no version file." - return - } - - read -r version release < "$pkg/version" && - printf '%s\n' "$pkg $version-$release" - done -} - -pkg_sources() { - # Download any remote package sources. The existence of local - # files is also checked. - log "[$1]: Downloading sources..." - - # Store each downloaded source in named after the package it - # belongs to. This avoid conflicts between two packages having a - # source of the same name. - mkdir -p "$src_dir/$1" && cd "$src_dir/$1" - - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$1") - - while read -r src _; do - case $src in - # Git repository. - git:*) - git clone "${src##git:}" "$mak_dir" - ;; - - # Remote source. - *://*) - [ -f "${src##*/}" ] && { - log "[$1]: Found cached source '${src##*/}'." - continue - } - - wget "$src" || { - rm -f "${src##*/}" - die "[$1]: Failed to download $src." - } - ;; - - # Local files (Any source that is non-remote is assumed to be local). - *) - [ -f "$repo_dir/$src" ] || - die "[$1]: No local file '$src'." - - log "[$1]: Found local file '$src'." - ;; - esac - done < "$repo_dir/sources" -} - -pkg_extract() { - # Extract all source archives to the build diectory and copy over - # any local repository files. - log "[$1]: Extracting sources..." - - # Store each downloaded source in named after the package it - # belongs to. This avoid conflicts between two packages having a - # source of the same name. - mkdir -p "$mak_dir/$1" && cd "$mak_dir/$1" - - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$1") - - while read -r src dest; do - mkdir -p "./$dest" - - case $src in - # Do nothing as git repository was downloaded to the build - # diectory directly. - git:*) ;; - - # Only 'tar' archives are currently supported for extaction. - # Any other filetypes are simply copied to '$mak_dir' which - # allows you to extract them manually. - *://*.tar*|*://*.tgz) - tar xf "$src_dir/$1/${src##*/}" -C "./$dest" \ - --strip-components 1 \ - || die "[$1]: Couldn't extract ${src##*/}." - ;; - - # Local files (Any source that is non-remote is assumed to be local). - *) - [ -f "$repo_dir/$src" ] || die "[$1]: Local file $src not found." - cp -f "$repo_dir/$src" "./$dest" - ;; - esac - done < "$repo_dir/sources" -} - -pkg_depends() { - # Resolve all dependencies and install them in the right order. - - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$1") - - # This does a depth-first search. The deepest dependencies are - # listed first and then the parents in reverse order. - if pkg_list "$1" >/dev/null; then - # If a package is already installed but 'pkg_depends' was - # given an argument, add it to the list anyway. - [ "$2" ] && missing_deps="$missing_deps $1 " - else - case $missing_deps in - # Dependency is already in list, skip it. - *" $1 "*) ;; - - *) - # Recurse through the dependencies of the child - # packages. Keep doing this. - [ -f "$repo_dir/depends" ] && - while read -r dep _; do - pkg_depends "$dep" ||: - done < "$repo_dir/depends" - - # After child dependencies are added to the list, - # add the package which depends on them. - missing_deps="$missing_deps $1 " - ;; - esac - fi -} - -pkg_verify() { - # Verify all package checksums. This is achieved by generating - # a new set of checksums and then comparing those with the old - # set. - - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$1") - - # Generate a second set of checksums to compare against the - # repositorie's checksums for the package. - pkg_checksums .checksums "$1" - - # Compare the checksums using 'cmp'. - cmp -s "$repo_dir/.checksums" "$repo_dir/checksums" || { - log "[$1]: Checksum mismatch." - - # Instead of dying above, log it to the terminal. Also define a - # variable so we *can* die after all checksum files have been - # checked. - mismatch="$mismatch$1 " - } - - # The second set of checksums use a temporary file, we need to - # delete it. - rm -f "$repo_dir/.checksums" -} - -pkg_strip() { - # Strip package binaries and libraries. This saves space on the - # system as well as on the tarballs we ship for installation. - - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$1") - - # Package has stripping disabled, stop here. - [ -f "$repo_dir/nostrip" ] && return - - log "[$1]: Stripping binaries and libraries..." - - find "$pkg_dir/$1" -type f | while read -r binary; do - case $(file -bi "$binary") in - application/x-sharedlib*|application/x-pie-executable*) - strip_opts=--strip-unneeded - ;; - - application/x-archive*) strip_opts=--strip-debug ;; - application/x-executable*) strip_opts=--strip-all ;; - - *) continue ;; - esac - - # Suppress errors here as some binaries and libraries may - # fail to strip. This is OK. - strip "$strip_opts" "$binary" 2>/dev/null ||: - done -} - -pkg_manifest() ( - # Generate the package's manifest file. This is a list of each file - # and directory inside the package. The file is used when uninstalling - # packages, checking for package conflicts and for general debugging. - # - # This funcion runs as a subshell to avoid having to 'cd' back to the - # prior directory before being able to continue. - cd "$pkg_dir/$1" - - # Find all files and directories in the package. Directories are printed - # with a trailing forward slash '/'. The list is then reversed with - # directories appearing *after* their contents. - find . -mindepth 1 -type d -exec printf '%s/\n' {} + -or -print | - sort -r | sed -e ss.ss > "$pkg_dir/$1/var/db/kiss/$1/manifest" - - log "[$1]: Generated manifest." -) - -pkg_tar() { - # Create a tarball from the built package's files. - # This tarball also contains the package's database entry. - - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$1") - - # Read the version information to name the package. - read -r version release < "$repo_dir/version" - - # Create a tarball from the contents of the built package. - tar zpcf "$bin_dir/$1#$version-$release.tar.gz" -C "$pkg_dir/$1" . || - die "[$1]: Failed to create tarball." - - log "[$1]: Successfully created tarball." -} - -pkg_build() { - # Build packages and turn them into packaged tarballs. This function - # also checks checksums, downloads sources and ensure all dependencies - # are installed. - - # Resolve dependencies and generate a list. - # Send 'force' to 'pkg_depends' to always include the explicitly - # requested packages. - log "Resolving dependencies..." - for pkg; do pkg_depends "$pkg" force; done - - # Disable globbing with 'set -f' to ensure that the unquoted - # variable doesn't expand into anything nasty. - # shellcheck disable=2086,2046 - { - # Set the resolved dependency list as the function's arguments. - set -f - set -- $missing_deps - set +f - } - log "Installing: $*." - - for pkg; do pkg_lint "$pkg"; done - for pkg; do - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$pkg") - - # Ensure that checksums exist prior to building the package. - [ -f "$repo_dir/checksums" ] || { - log "[$pkg]: Checksums are missing." - - # Instead of dying above, log it to the terminal. Also define a - # variable so we *can* die after all checksum files have been - # checked. - no_checkums="$no_checkums$pkg " - } - done - - # Die here as packages without checksums were found above. - [ "$no_checkums" ] && - die "Run '$kiss checksum ${no_checkums% }' to generate checksums." - - for pkg; do pkg_sources "$pkg"; done - for pkg; do pkg_verify "$pkg"; done - - # Die here as packages with differing checksums were found above. - [ "$mismatch" ] && die "Checksum mismatch with: ${mismatch% }" - - log "Verified all checksums." - - log "Extracting all sources..." - for pkg; do pkg_extract "$pkg"; done - log "Extracted all sources." - - log "Building packages..." - for pkg; do - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$pkg") - - # Install built packages to a directory under the package name - # to avod collisions with other packages. - mkdir -p "$pkg_dir/$pkg/var/db/kiss" - - # Move to the build directory and call the build script. - (cd "$mak_dir/$pkg"; "$repo_dir/build" "$pkg_dir/$pkg") || - die "[$pkg]: Build failed." - - # Copy the repository files to the package directory. - # This acts as the database entry. - cp -Rf "$repo_dir" "$pkg_dir/$pkg/var/db/kiss/" - - log "[$pkg]: Sucessfully built package." - - # Create the manifest file early and make it empty. - # This ensure that the manifest is added to the manifest... - : > "$pkg_dir/$pkg/var/db/kiss/$pkg/manifest" - done - - log "Stripping packages..." - for pkg; do pkg_strip "$pkg"; done - log "Stripped all binaries and libraries." - - log "Generating package manifests..." - for pkg; do pkg_manifest "$pkg"; done - log "Generated all manifests." - - log "Creating package tarballs..." - for pkg; do pkg_tar "$pkg"; done - log "Created all packages." -} - -pkg_checksums() { - # Generate checksums for packages. - # This also downloads any remote sources. - checksum_file=$1 - shift - - for pkg; do - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$pkg") - - while read -r src _; do - case $src in - # Git repository. - # Skip checksums on git repositories. - git:*) ;; - - *) - # File is local to the package and is stored in the - # repository. - [ -f "$repo_dir/$src" ] && - src_path=$repo_dir/${src%/*} - - # File is remote and was downloaded. - [ -f "$src_dir/$pkg/${src##*/}" ] && - src_path=$src_dir/$pkg - - # Die here if source for some reason, doesn't exist. - [ "$src_path" ] || - die "[$pkg]: Couldn't find source '$src'." - - # An easy way to get 'sha256sum' to print with the basenames - # of files is to 'cd' to the file's directory beforehand. - (cd "$src_path" && sha256sum "${src##*/}") || - die "[$pkg]: Failed to generate checksums." - - # Unset this variable so it isn't used again on a failed - # source. There's no 'local' keyword in POSIX sh. - src_path= - ;; - esac - done < "$repo_dir/sources" > "$repo_dir/$checksum_file" - - log "[$pkg]: Generated/Verified checksums." - done -} - -pkg_conflicts() { - # Check to see if a package conflicts with another. - # This function takes a path to a KISS tarball as an argument. - log "Checking for package conflicts." - - # Extract manifest from the tarball and only extract files entries. - tar xf "$1" -O "./var/db/kiss/$pkg_name/manifest" | - while read -r line; do - [ "${line%%*/}" ] && printf '%s\n' "$line" >> "$cac_dir/manifest-$pid" - done ||: - - # Compare extracted manifest to all installed manifests. - # If there are matching lines (files) there is a package conflict. - for db in "$KISS_ROOT/var/db/kiss/"*; do - [ "$pkg_name" = "${db##*/}" ] && continue - - grep -Fxf "$cac_dir/manifest-$pid" "$db/manifest" 2>/dev/null && - die "Package '$pkg_name' conflicts with '${db##*/}'." - done - - # Remove this temporary file as we no longer need it. - rm -f "$cac_dir/manifest-$pid" -} - -pkg_remove() { - # Remove a package and all of its files. The '/etc' directory - # is handled differently and configuration files are *not* - # overwritten. - - # Create a backup of 'rm' and 'rmdir' so they aren't removed - # during package removal. This ensures that an upgrade to 'busybox' - # or your coreutils of choice doesn't break the package manager. - cp "$(command -v rm)" "$cac_dir" - cp "$(command -v rmdir)" "$cac_dir" - - for pkg; do - # The package is not installed, don't do anything. - pkg_list "$pkg" >/dev/null || continue - - while read -r file; do - # The file is in '/etc' skip it. This prevents the package - # manager from removing user edited config files. - [ "${file##/etc/*}" ] || continue - - if [ -d "$KISS_ROOT/$file" ]; then - "$cac_dir/rmdir" "$KISS_ROOT/$file" 2>/dev/null || continue - else - "$cac_dir/rm" -f -- "$KISS_ROOT/$file" || - log "Failed to remove '$file'." - fi - done < "$KISS_ROOT/var/db/kiss/$pkg/manifest" - - log "Successfully removed '$pkg'." - done -} - -pkg_install() { - # Install a built package tarball. - - for pkg; do - # Install can also take the full path to a tarball. - # We don't need to check the repository if this is the case. - if [ -f "$pkg" ]; then - tar_file=$pkg - - else - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$pkg") - - # Read the version information to name the package. - read -r version release < "$repo_dir/version" - - # Construct the name of the package tarball. - tar_name=$pkg\#$version-$release.tar.gz - - [ -f "$bin_dir/$tar_name" ] || - die "Package '$pkg' has not been built." \ - "Run '$kiss build $pkg'." - - tar_file=$bin_dir/$tar_name - fi - - # Figure out which package the tarball installs by checking for - # a database entry inside the tarball. If no database entry exists, - # exit here as the tarball is *most likely* not a KISS package. - { - pkg_name=$(tar tf "$tar_file" | grep -x "\./var/db/kiss/.*/version") - pkg_name=${pkg_name%/*} - pkg_name=${pkg_name##*/} - } || die "'$tar_file' is not a valid KISS package." - - pkg_conflicts "$tar_file" - - # Extract the tarball early to catch any errors before installation - # begins. The package manager uninstalls the previous package during - # an upgrade so any errors need to be caught ASAP. - tar pxf "$tar_file" -C "$tar_dir/" || - die "[$pkg]: Failed to extract tarball." - - # Create a backup of 'mv', 'mkdir' and 'find' so they aren't removed - # during package removal. This ensures that an upgrade to 'busybox' or - # your coreutils of choice doesn't break the package manager. - cp "$(command -v mv)" "$cac_dir" - cp "$(command -v mkdir)" "$cac_dir" - cp "$(command -v find)" "$cac_dir" - - log "Removing previous version of package if it exists." - pkg_remove "$pkg_name" - - # Installation works by unpacking the tarball to a specified location, - # manually running 'mkdir' to create each directory and finally, using - # 'mv' to move each file. - cd "$tar_dir" - - # Create all of the package's directories. - # Optimization: Only find the deepest directories. - "$cac_dir/find" . -type d -links -3 -prune | while read -r dir; do - "$cac_dir/mkdir" -p "$KISS_ROOT/${dir#./}" - done - - # Move all package files to '$KISS_ROOT'. - "$cac_dir/find" ./ -mindepth 1 -not -type d | while read -r file; do - rpath=${file#.} - - # Don't overwrite existing '/etc' files. - [ -z "${rpath##/etc/*}" ] && - [ -f "$KISS_ROOT/${rpath%/*}/${file##*/}" ] && - return - - "$cac_dir/mv" "$file" "$KISS_ROOT/${rpath%/*}" - done - - # Run the post install script and suppress errors. If it exists, - # it will run, else nothing will happen. - "$KISS_ROOT/var/db/kiss/$pkg_name/post-install" 2>/dev/null ||: - - log "Successfully installed '$pkg_name'." - done -} - -pkg_updates() { - # Check all installed packages for updates. So long as the installed - # version and the version in the repositories differ, it's considered - # an update. - for pkg in "$KISS_ROOT/var/db/kiss/"*; do - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "${pkg##*/}") - - # Read version and release information from the installed packages - # and repository. - read -r db_ver db_rel < "$pkg/version" - read -r re_ver re_rel < "$repo_dir/version" - - # Compare installed packages to repository packages. - [ "$db_ver-$db_rel" != "$re_ver-$re_rel" ] && - printf '%s\n' "${pkg##*/} $re_ver-$re_rel" - done -} - -setup_caching() { - # Setup the host machine for the package manager. Create any - # directories which need to exist and set variables for easy - # access to them. - - # Main cache directory (~/.cache/kiss/) typically. - mkdir -p "${cac_dir:=${XDG_CACHE_HOME:=$HOME/.cache}/kiss}" || - die "Couldn't create cache directory ($cac_dir)." - - # Build directory. - mkdir -p "${mak_dir:=$cac_dir/build-$pid}" || - die "Couldn't create build directory ($mak_dir)." - - # Package directory. - mkdir -p "${pkg_dir:=$cac_dir/pkg-$pid}" || - die "Couldn't create package directory ($pkg_dir)." - - # Tar directory. - mkdir -p "${tar_dir:=$cac_dir/extract-$pid}" || - die "Couldn't create tar directory ($tar_dir)." - - # Source directory. - mkdir -p "${src_dir:=$cac_dir/sources}" || - die "Couldn't create source directory ($src_dir)." - - # Binary directory. - mkdir -p "${bin_dir:=$cac_dir/bin}" || - die "Couldn't create binary directory ($bin_dir)." -} - -pkg_clean() { - # Clean up on exit or error. This removes everything related - # to the build. - - # Remove temporary directories. - rm -rf -- "$mak_dir" "$pkg_dir" "$tar_dir" - - # Remove cached commands. - rm -f -- "$cac_dir/find" "$cac_dir/mv" "$cac_dir/mkdir" \ - "$cac_dir/rm" "$cac_dir/rmdir" -} - -root_check() { - # Ensure that the user has write permissions to '$KISS_ROOT'. - # When this variable is empty, a value of '/' is assumed. - [ -w "$KISS_ROOT/" ] || \ - die "No write permissions to '${KISS_ROOT:-/}'." \ - "You may need to run '$kiss' as root." -} - -args() { - # Parse script arguments manually. POSIX 'sh' has no 'getopts' - # or equivalent built in. This is rather easy to do in our case - # since the first argument is always an "action" and the arguments - # that follow are all package names. - - # Actions can be abbreviated to their first letter. This saves - # keystrokes once you memorize themand it also has the side-effect - # of "correcting" spelling mistakes assuming the first letter is - # right. - case $1 in - # Build the list of packages. - b*) - shift - [ "$1" ] || die "'kiss build' requires an argument." - pkg_build "$@" - ;; - - # Generate checksums for packages. - c*) - shift - [ "$1" ] || die "'kiss checksum' requires an argument." - - for pkg; do pkg_lint "$pkg"; done - for pkg; do pkg_sources "$pkg"; done - - pkg_checksums checksums "$@" - ;; - - # Install packages. - i*) - shift - [ "$1" ] || die "'kiss install' requires an argument." - root_check - pkg_install "$@" - ;; - - # Remove packages. - r*) - shift - [ "$1" ] || die "'kiss remove' requires an argument." - root_check - pkg_remove "$@" - ;; - - # List installed packages. - l*) - shift - pkg_list "$@" - ;; - - # Upgrade packages. - u*) - pkg_updates - ;; - - # Print version and exit. - v*) - log "$kiss 0.1.10" - ;; - - # Catch all invalid arguments as well as - # any help related flags (-h, --help, help). - *) - log "$kiss [b|c|i|l|r|u] [pkg]" \ - "build: Build a package." \ - "checksum: Generate checksums." \ - "install: Install a package (Runs build if needed)." \ - "list: List packages." \ - "remove: Remove a package." \ - "update: Check for updates." - ;; - esac -} - -main() { - # Store the script name in a variable and use it everywhere - # in place of 'kiss'. This allows the script name to be changed - # easily. - kiss=${0##*/} - - # The PID of the current shell process is used to isolate directories - # to each specific KISS instance. This allows multiple package manager - # instances to be run at once. Store the value in another variable so - # that it doesn't change beneath us. - pid=$$ - - # Catch errors and ensure that build files and directories are cleaned - # up before we die. This occurs on 'Ctrl+C' as well as sucess and error. - trap pkg_clean EXIT INT - - setup_caching - args "$@" -} - -main "$@" From 1a8fcdc07a6b37c034ecf11d8180f0829f30e38e Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sun, 30 Jun 2019 09:23:04 +0300 Subject: [PATCH 31/43] kiss: Install any pre-built binaries if they exist. --- kiss | 81 ++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/kiss b/kiss index d88b035..5b7fec1 100755 --- a/kiss +++ b/kiss @@ -11,7 +11,7 @@ # an error is optional or required. # # Where possible the package manager should "error first". -# Check things first, die is necessary and continue if all is well. +# Check things first, die if necessary and continue if all is well. # # The code below conforms to shellcheck's rules. However, some # lint errors *are* disabled as they relate to unexpected @@ -114,7 +114,7 @@ pkg_list() { [ -f "$pkg/version" ] || { log "Warning: Package '$pkg' has no version file." - return + continue } read -r version release < "$pkg/version" && @@ -353,6 +353,11 @@ pkg_build() { log "Resolving dependencies..." for pkg; do pkg_depends "$pkg" force; done + # Store the explicit packages so we can handle them differently + # below. Dependencies are automatically installed but packages + # passed to KISS aren't. + explicit_packages=" $* " + # Disable globbing with 'set -f' to ensure that the unquoted # variable doesn't expand into anything nasty. # shellcheck disable=2086,2046 @@ -363,6 +368,38 @@ pkg_build() { set +f } log "Installing: $*." + log "Checking to see if any dependencies have already been built..." + log "Installing any pre-built dependencies..." + + # Install any pre-built dependencies if they exist in the binary + # directory and are up to date. + for pkg; do + # Don't check for pre-built package if it was passed to KISS + # directly. + case $explicit_packages in *" $pkg "*) continue ;; esac + + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + # Figure out the version and release. + read -r version release < "$repo_dir/version" + + # Remove the current package from the package list. + shift + + # Install any pre-built binaries if they exist. + [ -f "$bin_dir/$pkg#$version-$release.tar.gz" ] && { + log "[$pkg]: Found pre-built binary." + pkg_install "$bin_dir/$pkg#$version-$release.tar.gz" + continue + } + + # Add the removed package back to the list if it doesn't + # have a pre-built binary. + set -- "$pkg" "$@" + done for pkg; do pkg_lint "$pkg"; done for pkg; do @@ -390,15 +427,10 @@ pkg_build() { for pkg; do pkg_verify "$pkg"; done # Die here as packages with differing checksums were found above. - [ "$mismatch" ] && die "Checksum mismatch with: ${mismatch% }" + [ "$mismatch" ] && + die "Checksum mismatch with: ${mismatch% }" - log "Verified all checksums." - - log "Extracting all sources..." for pkg; do pkg_extract "$pkg"; done - log "Extracted all sources." - - log "Building packages..." for pkg; do # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data @@ -422,19 +454,20 @@ pkg_build() { # Create the manifest file early and make it empty. # This ensure that the manifest is added to the manifest... : > "$pkg_dir/$pkg/var/db/kiss/$pkg/manifest" + + pkg_strip "$pkg" + pkg_manifest "$pkg" + pkg_tar "$pkg" + + # Install only dependencies of passed packages. + case $explicit_packages in + *" $pkg "*) continue ;; + *) pkg_install "$pkg" ;; + esac done - log "Stripping packages..." - for pkg; do pkg_strip "$pkg"; done - log "Stripped all binaries and libraries." - - log "Generating package manifests..." - for pkg; do pkg_manifest "$pkg"; done - log "Generated all manifests." - - log "Creating package tarballs..." - for pkg; do pkg_tar "$pkg"; done - log "Created all packages." + log "Successfully built all packages." + log "Run '$kiss i $*' to install them." } pkg_checksums() { @@ -522,7 +555,10 @@ pkg_remove() { for pkg; do # The package is not installed, don't do anything. - pkg_list "$pkg" >/dev/null || continue + pkg_list "$pkg" >/dev/null || { + log "[$pkg]: Not installed." + continue + } while read -r file; do # The file is in '/etc' skip it. This prevents the package @@ -688,6 +724,9 @@ pkg_clean() { # Remove cached commands. rm -f -- "$cac_dir/find" "$cac_dir/mv" "$cac_dir/mkdir" \ "$cac_dir/rm" "$cac_dir/rmdir" + + # Remove temporary files. + rm -f "$repo_dir/.checksums" } root_check() { From 4c39f6e0be3b04eb45f6b634f99dbb2fb9f35894 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sun, 30 Jun 2019 09:47:38 +0300 Subject: [PATCH 32/43] kiss: Fix dependency chain. --- kiss | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kiss b/kiss index 5b7fec1..ed77665 100755 --- a/kiss +++ b/kiss @@ -376,7 +376,13 @@ pkg_build() { for pkg; do # Don't check for pre-built package if it was passed to KISS # directly. - case $explicit_packages in *" $pkg "*) continue ;; esac + case $explicit_packages in + *" $pkg "*) + shift + set -- "$@" "$pkg" + continue + ;; + esac # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data @@ -430,8 +436,9 @@ pkg_build() { [ "$mismatch" ] && die "Checksum mismatch with: ${mismatch% }" - for pkg; do pkg_extract "$pkg"; done for pkg; do + pkg_extract "$pkg" + # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data # structure. From 875da0c6f7e541a723f55bd08b99cb36db916d15 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sun, 30 Jun 2019 10:39:38 +0300 Subject: [PATCH 33/43] kiss: Fix tar errors. --- kiss | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kiss b/kiss index ed77665..b29bb93 100755 --- a/kiss +++ b/kiss @@ -404,7 +404,7 @@ pkg_build() { # Add the removed package back to the list if it doesn't # have a pre-built binary. - set -- "$pkg" "$@" + set -- "$@" "$pkg" done for pkg; do pkg_lint "$pkg"; done @@ -531,7 +531,7 @@ pkg_conflicts() { log "Checking for package conflicts." # Extract manifest from the tarball and only extract files entries. - tar xf "$1" -O "./var/db/kiss/$pkg_name/manifest" | + tar xf "$1" -O "./var/db/kiss/$2/manifest" | while read -r line; do [ "${line%%*/}" ] && printf '%s\n' "$line" >> "$cac_dir/manifest-$pid" done ||: @@ -539,10 +539,10 @@ pkg_conflicts() { # Compare extracted manifest to all installed manifests. # If there are matching lines (files) there is a package conflict. for db in "$KISS_ROOT/var/db/kiss/"*; do - [ "$pkg_name" = "${db##*/}" ] && continue + [ "$2" = "${db##*/}" ] && continue grep -Fxf "$cac_dir/manifest-$pid" "$db/manifest" 2>/dev/null && - die "Package '$pkg_name' conflicts with '${db##*/}'." + die "Package '$2' conflicts with '${db##*/}'." done # Remove this temporary file as we no longer need it. @@ -590,7 +590,7 @@ pkg_install() { for pkg; do # Install can also take the full path to a tarball. # We don't need to check the repository if this is the case. - if [ -f "$pkg" ]; then + if [ -f "$pkg" ] && [ -z "${pkg%%*.tar.gz}" ] ; then tar_file=$pkg else @@ -621,7 +621,7 @@ pkg_install() { pkg_name=${pkg_name##*/} } || die "'$tar_file' is not a valid KISS package." - pkg_conflicts "$tar_file" + pkg_conflicts "$tar_file" "$pkg_name" # Extract the tarball early to catch any errors before installation # begins. The package manager uninstalls the previous package during From 41f442fc5f6f0d7d5b567432ee2bfee6525a244e Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sun, 30 Jun 2019 10:58:53 +0300 Subject: [PATCH 34/43] kiss: Update log outputs. --- kiss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kiss b/kiss index b29bb93..5b6deeb 100755 --- a/kiss +++ b/kiss @@ -474,7 +474,7 @@ pkg_build() { done log "Successfully built all packages." - log "Run '$kiss i $*' to install them." + log "Run '$kiss i $*' to install the built packages." } pkg_checksums() { @@ -528,7 +528,7 @@ pkg_checksums() { pkg_conflicts() { # Check to see if a package conflicts with another. # This function takes a path to a KISS tarball as an argument. - log "Checking for package conflicts." + log "[$2]: Checking for package conflicts." # Extract manifest from the tarball and only extract files entries. tar xf "$1" -O "./var/db/kiss/$2/manifest" | From c843be52fc86dc0b281ccdce051c5939f1e8b5c4 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sun, 30 Jun 2019 10:59:10 +0300 Subject: [PATCH 35/43] kiss: version bump --- kiss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kiss b/kiss index 5b6deeb..03c3b5a 100755 --- a/kiss +++ b/kiss @@ -802,7 +802,7 @@ args() { # Print version and exit. v*) - log "$kiss 0.1.10" + log "$kiss 0.2.0" ;; # Catch all invalid arguments as well as From b499e84c5bc14b0d864f87eacf555b39d474d9fc Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sun, 30 Jun 2019 11:35:54 +0300 Subject: [PATCH 36/43] kiss: Added more messages. --- kiss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kiss b/kiss index 03c3b5a..d400d59 100755 --- a/kiss +++ b/kiss @@ -276,6 +276,7 @@ pkg_verify() { pkg_strip() { # Strip package binaries and libraries. This saves space on the # system as well as on the tarballs we ship for installation. + log "[$1]: Stripping binaries and libraries..." # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data @@ -309,7 +310,8 @@ pkg_manifest() ( # Generate the package's manifest file. This is a list of each file # and directory inside the package. The file is used when uninstalling # packages, checking for package conflicts and for general debugging. - # + log "[$1]: Generating manifest..." + # This funcion runs as a subshell to avoid having to 'cd' back to the # prior directory before being able to continue. cd "$pkg_dir/$1" @@ -326,6 +328,7 @@ pkg_manifest() ( pkg_tar() { # Create a tarball from the built package's files. # This tarball also contains the package's database entry. + log "[$1]: Creating tarball..." # Find the package's repository files. This needs to keep # happening as we can't store this data in any kind of data From de62a28c476f8effa6553f518f870d3cc3332be3 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Sun, 30 Jun 2019 11:44:19 +0300 Subject: [PATCH 37/43] kiss: No more license checks. --- kiss | 1 - 1 file changed, 1 deletion(-) diff --git a/kiss b/kiss index d400d59..a82a5a5 100755 --- a/kiss +++ b/kiss @@ -43,7 +43,6 @@ pkg_lint() { [ -f sources ] || die "Sources file not found." [ -x build ] || die "Build file not found or not executable." - [ -s licenses ] || die "License file not found or empty." [ -s version ] || die "Version file not found or empty." # Ensure that the release field in the version file is set From 83b206f05ac2f85f4b9a3d28f2965ac71227a177 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Wed, 3 Jul 2019 16:35:14 +0300 Subject: [PATCH 38/43] kiss-new: polish --- kiss | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/kiss b/kiss index a82a5a5..525f932 100755 --- a/kiss +++ b/kiss @@ -438,6 +438,8 @@ pkg_build() { [ "$mismatch" ] && die "Checksum mismatch with: ${mismatch% }" + # Finally build and create tarballs for all passed packages and + # dependencies. for pkg; do pkg_extract "$pkg" @@ -475,8 +477,8 @@ pkg_build() { esac done - log "Successfully built all packages." - log "Run '$kiss i $*' to install the built packages." + log "Successfully built package(s)." + log "Run '$kiss i $*' to install the built package(s)." } pkg_checksums() { @@ -578,11 +580,11 @@ pkg_remove() { "$cac_dir/rmdir" "$KISS_ROOT/$file" 2>/dev/null || continue else "$cac_dir/rm" -f -- "$KISS_ROOT/$file" || - log "Failed to remove '$file'." + log "[$pkg]: Failed to remove '$file'." fi done < "$KISS_ROOT/var/db/kiss/$pkg/manifest" - log "Successfully removed '$pkg'." + log "[$pkg]: Removed successfully." done } @@ -617,11 +619,11 @@ pkg_install() { # Figure out which package the tarball installs by checking for # a database entry inside the tarball. If no database entry exists, # exit here as the tarball is *most likely* not a KISS package. - { - pkg_name=$(tar tf "$tar_file" | grep -x "\./var/db/kiss/.*/version") - pkg_name=${pkg_name%/*} - pkg_name=${pkg_name##*/} - } || die "'$tar_file' is not a valid KISS package." + pkg_name=$(tar tf "$tar_file" | grep -x "\./var/db/kiss/.*/version") || + die "'${tar_file##*/}' is not a valid KISS package." + + pkg_name=${pkg_name%/*} + pkg_name=${pkg_name##*/} pkg_conflicts "$tar_file" "$pkg_name" @@ -629,7 +631,7 @@ pkg_install() { # begins. The package manager uninstalls the previous package during # an upgrade so any errors need to be caught ASAP. tar pxf "$tar_file" -C "$tar_dir/" || - die "[$pkg]: Failed to extract tarball." + die "[$pkg_name]: Failed to extract tarball." # Create a backup of 'mv', 'mkdir' and 'find' so they aren't removed # during package removal. This ensures that an upgrade to 'busybox' or @@ -638,7 +640,7 @@ pkg_install() { cp "$(command -v mkdir)" "$cac_dir" cp "$(command -v find)" "$cac_dir" - log "Removing previous version of package if it exists." + log "[$pkg_name]: Removing previous version of package if it exists." pkg_remove "$pkg_name" # Installation works by unpacking the tarball to a specified location, @@ -668,7 +670,7 @@ pkg_install() { # it will run, else nothing will happen. "$KISS_ROOT/var/db/kiss/$pkg_name/post-install" 2>/dev/null ||: - log "Successfully installed '$pkg_name'." + log "[$pkg_name]: Installed successfully." done } @@ -837,7 +839,10 @@ main() { # up before we die. This occurs on 'Ctrl+C' as well as sucess and error. trap pkg_clean EXIT INT + # Create the required temporary directories and set the variables + # which point to them. setup_caching + args "$@" } From d7ca9adc75f9e45ef8489a54934851db7907c046 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Wed, 3 Jul 2019 16:43:46 +0300 Subject: [PATCH 39/43] kiss-new: Polish --- kiss | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/kiss b/kiss index 525f932..0dcd4a4 100755 --- a/kiss +++ b/kiss @@ -41,9 +41,10 @@ pkg_lint() { cd "$pkg_location" || die "'$pkg_location' not accessible" - [ -f sources ] || die "Sources file not found." - [ -x build ] || die "Build file not found or not executable." - [ -s version ] || die "Version file not found or empty." + [ -f sources ] || die "[$1]: Sources file not found." + [ -x build ] || die "[$1]: Build file not found or not executable." + [ -s version ] || die "[$1]: Version file not found or empty." + [ -f checksums ] || die "[$1]: Checksums file not found." # Ensure that the release field in the version file is set # to something. @@ -410,27 +411,6 @@ pkg_build() { done for pkg; do pkg_lint "$pkg"; done - for pkg; do - # Find the package's repository files. This needs to keep - # happening as we can't store this data in any kind of data - # structure. - repo_dir=$(pkg_search "$pkg") - - # Ensure that checksums exist prior to building the package. - [ -f "$repo_dir/checksums" ] || { - log "[$pkg]: Checksums are missing." - - # Instead of dying above, log it to the terminal. Also define a - # variable so we *can* die after all checksum files have been - # checked. - no_checkums="$no_checkums$pkg " - } - done - - # Die here as packages without checksums were found above. - [ "$no_checkums" ] && - die "Run '$kiss checksum ${no_checkums% }' to generate checksums." - for pkg; do pkg_sources "$pkg"; done for pkg; do pkg_verify "$pkg"; done From 7cbe4185f0cd76576198ebb2a9347d3645031780 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Wed, 3 Jul 2019 16:44:48 +0300 Subject: [PATCH 40/43] kiss-new: Polish --- kiss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kiss b/kiss index 0dcd4a4..db8c205 100755 --- a/kiss +++ b/kiss @@ -44,7 +44,7 @@ pkg_lint() { [ -f sources ] || die "[$1]: Sources file not found." [ -x build ] || die "[$1]: Build file not found or not executable." [ -s version ] || die "[$1]: Version file not found or empty." - [ -f checksums ] || die "[$1]: Checksums file not found." + [ -f checksums ] || die "[$1]: Checksums file not found ('$0 checksums $1')." # Ensure that the release field in the version file is set # to something. From 292816b697bedd0d6dc2ff805d8e45ed37b216aa Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Wed, 3 Jul 2019 17:29:57 +0300 Subject: [PATCH 41/43] kiss: oops --- kiss | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/kiss b/kiss index db8c205..08383d9 100755 --- a/kiss +++ b/kiss @@ -44,7 +44,6 @@ pkg_lint() { [ -f sources ] || die "[$1]: Sources file not found." [ -x build ] || die "[$1]: Build file not found or not executable." [ -s version ] || die "[$1]: Version file not found or empty." - [ -f checksums ] || die "[$1]: Checksums file not found ('$0 checksums $1')." # Ensure that the release field in the version file is set # to something. @@ -370,7 +369,7 @@ pkg_build() { set -- $missing_deps set +f } - log "Installing: $*." + log "Building: $*." log "Checking to see if any dependencies have already been built..." log "Installing any pre-built dependencies..." @@ -411,6 +410,27 @@ pkg_build() { done for pkg; do pkg_lint "$pkg"; done + for pkg; do + # Find the package's repository files. This needs to keep + # happening as we can't store this data in any kind of data + # structure. + repo_dir=$(pkg_search "$pkg") + + # Ensure that checksums exist prior to building the package. + [ -f "$repo_dir/checksums" ] || { + log "[$pkg]: Checksums are missing." + + # Instead of dying above, log it to the terminal. Also define a + # variable so we *can* die after all checksum files have been + # checked. + no_checkums="$no_checkums$pkg " + } + done + + # Die here as packages without checksums were found above. + [ "$no_checkums" ] && + die "Run '$kiss checksum ${no_checkums% }' to generate checksums." + for pkg; do pkg_sources "$pkg"; done for pkg; do pkg_verify "$pkg"; done From 6a3b3119b863d21436438e86ad9db43fc33d3353 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Wed, 3 Jul 2019 17:31:00 +0300 Subject: [PATCH 42/43] kiss: new --- kiss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kiss b/kiss index 08383d9..8ceddd5 100755 --- a/kiss +++ b/kiss @@ -37,9 +37,9 @@ pkg_lint() { # Check that each mandatory file in the package entry exists. log "[$1]: Checking repository files..." - pkg_location=$(pkg_search "$1") + repo_dir=$(pkg_search "$1") - cd "$pkg_location" || die "'$pkg_location' not accessible" + cd "$repo_dir" || die "'$repo_dir' not accessible" [ -f sources ] || die "[$1]: Sources file not found." [ -x build ] || die "[$1]: Build file not found or not executable." From cabf422cd9c8d00c86b29af3451f25fc20235fd2 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Thu, 4 Jul 2019 09:13:44 +0300 Subject: [PATCH 43/43] kiss: clean up --- kiss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kiss b/kiss index 8ceddd5..d4676b5 100755 --- a/kiss +++ b/kiss @@ -201,7 +201,9 @@ pkg_extract() { # Local files (Any source that is non-remote is assumed to be local). *) - [ -f "$repo_dir/$src" ] || die "[$1]: Local file $src not found." + [ -f "$repo_dir/$src" ] || + die "[$1]: Local file $src not found." + cp -f "$repo_dir/$src" "./$dest" ;; esac