From 8763cd2b8232c470770bbd36e05eb4eac3e1f9cd Mon Sep 17 00:00:00 2001
From: 0x1eef <0x1eef@protonmail.com>
Date: Sun, 19 Mar 2023 02:31:25 -0300
Subject: [PATCH] Add support for incremental updates
The 'portzap install' command would always install the entire ports
tree, whether for the first time or on a subsequent update where
copying the entire tree isn't neccessary.
This change is an attempt at only copying ports that have been modified
in some way. Rather than dealing with modifications file by file, a port
that is found to have modifications has its entire directory copied. This
makes life easier but is a bit slower.
---
README.md | 9 +++--
bin/portzap | 59 ++++++++++++++++--------------
libexec/portzap/functions/fs.sh | 24 ++++++++++++
libexec/portzap/functions/git.sh | 11 ++++++
libexec/portzap/functions/perms.sh | 21 +++++++++++
5 files changed, 93 insertions(+), 31 deletions(-)
create mode 100755 libexec/portzap/functions/fs.sh
create mode 100644 libexec/portzap/functions/git.sh
create mode 100644 libexec/portzap/functions/perms.sh
diff --git a/README.md b/README.md
index 7862081..e6d7085 100644
--- a/README.md
+++ b/README.md
@@ -5,19 +5,20 @@ portzap is a utility for staying up to date with the
The utility stores a transient copy of the ports tree in `/home/_portzap/ports/`.
The transient copy can be created, and updated by an unprivileged user account
who is a member of the `_portzap` group. The transient copy can then be installed
-into the `/usr/ports` directory by root.
+into the `/usr/ports/` directory by root.
## Usage
* `portzap clone`
- This command clones HardenedBSD's ports tree into `/home/_portzap/ports`.
+ This command clones HardenedBSD's ports tree into `/home/_portzap/ports/`.
* `portzap pull`
- This command pulls updates into `/home/_portzap/ports`.
+ This command pulls updates into `/home/_portzap/ports/`.
* `portzap install`
This command should be run as root.
- The command installs `/home/_portzap/ports` into `/usr/ports`.
+ The command installs `/home/_portzap/ports/` into `/usr/ports/`.
+ After the first installation, future installations try to save time by being incremental.
## Sources
diff --git a/bin/portzap b/bin/portzap
index 9531b89..7a8ce29 100755
--- a/bin/portzap
+++ b/bin/portzap
@@ -6,6 +6,7 @@
# Configuration
ports_url="https://git.hardenedbsd.org/hardenedbsd/ports.git"
ports_dir="/usr/ports/"
+portzap_rev="$ports_dir/.portzap_last_rev"
portzap_dir="/home/_portzap/ports"
libexec_dir=$(realpath $(dirname $0)/../libexec/portzap/)
@@ -17,6 +18,9 @@ pull_mask=007
##
# Utils
+. $libexec_dir/functions/perms.sh
+. $libexec_dir/functions/fs.sh
+. $libexec_dir/functions/git.sh
exit_on_missing_deps() {
deps=$1
for dep in $deps; do
@@ -28,24 +32,6 @@ exit_on_missing_deps() {
done
}
-has_portzap_access() {
- groups=$(id -Gn)
- in_group=1
- for g in $groups; do
- if [ $g = "_portzap" ];
- then
- in_group=0
- fi
- done
- return $in_group
-}
-
-user_is_not_root() {
- user_id=$(id -u)
- result=$(test $user_id -ne "0")
- return $result
-}
-
##
# Commands
help() {
@@ -98,16 +84,35 @@ install() {
exit 1
fi
+ echo "Please wait..."
cd $portzap_dir
- find -s . -maxdepth 1 -type f \
- \( -not -name ".gitignore" \) \
- \( -not -name ".arcconfig" \) \
- -exec $libexec_dir/install-file $ports_dir {} +
- find -s . -maxdepth 1 -type d \
- \( -not -name "." \) \
- \( -not -name ".git" \) \
- \( -not -name ".hooks" \) \
- -exec $libexec_dir/install-directory $ports_dir $libexec_dir {} +
+ if [ -e "$portzap_rev" ]; then
+ rev=$(cat $portzap_rev)
+ port_dirs=$(modified_ports $rev)
+ rm_files=$(git diff --name-only --diff-filter=D $rev..HEAD)
+ for file in $rm_files; do
+ rm $ports_dir/$file
+ done
+ for dir in $port_dirs; do
+ if [ -f "$dir" ]; then
+ $libexec_dir/install-file $ports_dir $dir
+ else
+ $libexec_dir/install-directory $ports_dir $libexec_dir $dir
+ fi
+ done
+ else
+ find -s . -maxdepth 1 -type f \
+ \( -not -name ".gitignore" \) \
+ \( -not -name ".arcconfig" \) \
+ -exec $libexec_dir/install-file $ports_dir {} +
+ find -s . -maxdepth 1 -type d \
+ \( -not -name "." \) \
+ \( -not -name ".git" \) \
+ \( -not -name ".hooks" \) \
+ -exec $libexec_dir/install-directory $ports_dir $libexec_dir {} +
+ fi
+ echo $(git rev-parse HEAD) > $portzap_rev
+ echo "Done."
}
case $1 in
diff --git a/libexec/portzap/functions/fs.sh b/libexec/portzap/functions/fs.sh
new file mode 100755
index 0000000..db4d188
--- /dev/null
+++ b/libexec/portzap/functions/fs.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+##
+# Returns the depth of a path
+port_depth() {
+ p=$1
+ result=$(echo $p | tr '/' '\n' | grep . | wc -l)
+ return $result
+}
+
+##
+# Returns the entry point for a port relative to /usr/ports
+# (eg: ./ftp/curl/)
+port_dirname() {
+ p=$1
+ port_depth "$p"
+ depth=$?
+ if [ $depth -gt 2 ]; then
+ p=$(dirname $p)
+ port_dirname "$p"
+ else
+ echo $p
+ fi
+}
diff --git a/libexec/portzap/functions/git.sh b/libexec/portzap/functions/git.sh
new file mode 100644
index 0000000..79715f1
--- /dev/null
+++ b/libexec/portzap/functions/git.sh
@@ -0,0 +1,11 @@
+##
+# Returns a list of new, and modified files between two points / commits.
+modified_ports() {
+ rev=$1
+ files=$(git diff --name-only --diff-filter=AM $rev..HEAD)
+ for file in $files; do
+ dirs="$dirs $(port_dirname $file)"
+ done
+ dirs=$(echo $dirs | tr ' ' '\n' | uniq)
+ echo $dirs
+}
diff --git a/libexec/portzap/functions/perms.sh b/libexec/portzap/functions/perms.sh
new file mode 100644
index 0000000..2ba718e
--- /dev/null
+++ b/libexec/portzap/functions/perms.sh
@@ -0,0 +1,21 @@
+##
+# Returns true when current user is in _portzap group
+has_portzap_access() {
+ groups=$(id -Gn)
+ in_group=1
+ for g in $groups; do
+ if [ $g = "_portzap" ];
+ then
+ in_group=0
+ fi
+ done
+ return $in_group
+}
+
+##
+# Returns true when current user is root
+user_is_not_root() {
+ user_id=$(id -u)
+ result=$(test $user_id -ne "0")
+ return $result
+}