Compare commits

...

3 Commits

6 changed files with 143 additions and 121 deletions

View File

@ -10,24 +10,25 @@
.fam T
.fi
.SH DESCRIPTION
\fBpkgfoster\fP is a simple interactive script you can use to clean up orphaned packages (i.e.,
packages not listed in the "Depends on: " line of any other installed package). It uses
prt-cache by default, so remember to build the cache with "prt-get cache". You can also
use normal prt-get by modifying the PRT_GET variable. The bash array FOSTERED
is read from and written to ~/.config/pkgfoster.conf, as a means of preserving your choices
regarding which packages to keep. If you run \fBpkgfoster\fP as a non-root user, make sure the user
is able to elevate permissions using sudo, doas, or 'su -c'.
\fBpkgfoster\fP is a simple interactive script you can use to clean up orphaned
packages (i.e., packages not listed in the "Depends on: " line of any other installed
package). It uses prt-cache by default, so remember to build the cache with "prt-get
cache". You can also use normal prt-get by modifying the PRT_GET variable. The file
~/.config/pkgfoster.keep is used to preserve your decisions regarding which packages
to keep. If you run \fBpkgfoster\fP as a non-root user, make sure the user is able to
elevate permissions using sudo, doas, or 'su -c'.
.PP
Packages from the core collection are never considered for deletion, since some of them
might provide runtime dependencies yet not be listed in the "Depends on: " line. See
\fBPkgfile\fP(5) for an explanation of this practice.
.PP
Some packages flagged by \fBpkgfoster\fP might actually be optional (soft) dependencies of other
packages on your system. Use \fBrevdep\fP(1) to identify any breakage that you introduce by
removing these misidentified orphans, and rebuild the affected ports using
Some packages flagged by \fBpkgfoster\fP might actually be optional (soft)
dependencies of other packages on your system. Use \fBrevdep\fP(1) to identify any
breakage that you introduce by removing these misidentified orphans, and rebuild the
affected ports using
.B prt\-get update \-fr
(see \fBprt\-get\fP(8) for more detailed examples).
.SH AUTHORS
Jukka Heino <jukka@karsikkopuu.net>
.SH SEE ALSO
\fBprt\-cache\fP(8), \fBprt\-get\fP(8), \fBrevdep\fP(1)
\fBprt\-cache\fP(8), \fBprt\-get\fP(8), \fBPkgfile\fP(5), \fBrevdep\fP(1)

View File

@ -1,7 +1,7 @@
.\"
.\" prtsweep manual page.
.\" (C) 2e003 by Martin Opel
.\" Revised 2021 by John McQuah
.\" Revised 2022 by John McQuah
.\"
.TH prtsweep 1
.SH NAME
@ -9,7 +9,7 @@ prtsweep \- sweep old files from the ports directories
.SH SYNOPSIS
.PP
.B prtsweep
[\-a] [\-d] [\-n] [PORTDIR ...]
[\-a] [\-d] [\-n] [\-q] [PORTDIR ...]
.SH DESCRIPTION
The \fIprtsweep\fP perl script sweeps port directories, deleting unneeded files.
@ -17,7 +17,7 @@ The \fIprtsweep\fP perl script sweeps port directories, deleting unneeded files.
built package
.PP
.nf
name#version-release.pkg.tar.?z*
name#version-release.pkg.tar.*
.fi
.PP
All other files are removed. If a traversal of the ports collections in automatic mode

View File

@ -36,34 +36,28 @@ All items not included in the previous list WILL be deleted
You can choose to delete some of the above by passing the proper
option:
.TP
.B
-p
also remove the built package (current version)
.B \-p
remove the built package (current version)
.TP
.B
-b
also remove other built packages (older versions)
.B \-b
remove older builds (any package tarball that does NOT match
the current version)
.TP
.B
-d
also remove the add-on files provided by CRUX maintainers
.B \-d
remove add-on files provided by CRUX maintainers
(patches and initscripts downloaded with \fBports -u\fP)
.TP
.B
-s
also remove the upstream sources (those NOT obtained via \fBports -u\fP)
.B \-s
remove upstream sources (those NOT obtained via \fBports -u\fP)
.TP
.B
-t
.B \-t
(test mode) display the target files without deleting
.TP
.B
-q
.B \-q
(quiet mode) suppress messages about files that are kept;
basically the same as \fBprtwash [OPTIONS] | grep -v "keeping"\fP
.TP
.B
-a
.B \-a
(automatic mode) take the port collections from the
/etc/prt-get.conf file and do a recursive cleaning;
if omitted, the command line must contain at least one <path> specifying
@ -71,12 +65,14 @@ a valid port directory
.PP
Misc options
.TP
.B
-h
.B \-\-parse\-pkgmk\-conf
Revert to the v0.9 behaviour of parsing /etc/pkgmk.conf to determine the
compression mode of built packages
.TP
.B \-h
Display usage information
.TP
.B
-v
.B \-v
Display version
.SH ENVIRONMENT
In automatic mode, \fBprtwash\fP gets a list of repositories from
@ -93,11 +89,24 @@ Sources under CRUX version control are only deleted if you pass the
option \fB\-d\fP. Sources from upstream are only deleted if you pass the
option \fB\-s\fP.
.PP
The current version of the package is only deleted if you pass the
option \fB\-p\fP. Older versions of the package are only deleted if you
pass the option \fB\-b\fP. Note that if the compression mode defined
in /etc/pkgmk.conf has been changed since you last built the package,
\fBprtwash\fP will not recognize the tarball in its regexp search.
You can also delete the built package (current version) by passing
the option \fB\-p\fP. To select only older versions for deletion,
pass the option \fB\-b\fP instead. These two options operate independently,
allowing you to target four distinct subsets (including the empty set)
for deletion.
.PP
By passing the option \fB\-\-parse\-pkgmk\-conf\fP, you can modify the behaviour
of the pattern matching performed to detect built packages. By default, the
regexp searches are done with the patterns
.B /${name}#${version}.*.pkg.tar.(gz|lz|xz|bz2)/
and
.B /${name}#.*pkg.tar.(gz|lz|xz|bz2)/
using the variables defined in the \fBPkgfile\fP(5). Passing the option
.B \-\-parse\-pkgmk\-conf
will result in a more narrow regexp search, allowing the deletion of any package
tarball that uses a different compression algorithm than the one currently defined
in /etc/pkgmk.conf (which is how the v0.9 bash script worked).
.SH COMPARISON WITH OTHER UTILITIES
Because \fBprtwash\fP reads the location of port collections from \fB/etc/prt-get.conf\fP(5),
you can easily control which collections are cleaned in automatic mode by commenting

View File

@ -4,11 +4,11 @@
# Jukka Heino <jukka@karsikkopuu.net>
#
# pkgfoster is a simple script you can use to clean up orphaned packages (i.e.
# packages which no other package depends on). It uses prt-cache by default, so
# remember to build the cache with "prt-get cache". You can also use normal
# prt-get by modifying the PRT_GET variable. An array of packages to keep
# is stored in the FOSTERED variable. Packages from core are never considered
# for deletion.
# packages which do not appear in the "Depends on: " line of any other installed
# package). It uses prt-cache by default, so remember to build the cache with
# "prt-get cache". You can also use normal prt-get by modifying the PRT_GET
# variable. The file ~/.config/pkgfoster.keep contains a list of all packages
# you've decided to keep. Packages from core are never considered for deletion.
#
PRT_GET=prt-cache
@ -26,17 +26,20 @@ else
fi
BASE=$(awk '/^[[:space:]]*prtdir.*\/core/ {print $2}' /etc/prt-get.conf)
CONF=${XDG_CONFIG_HOME:="$HOME/.config"}/pkgfoster.conf
[ -f $CONF ] && . $CONF || mkdir -p ${XDG_CONFIG_HOME}
CONF="${XDG_CONFIG_HOME:="$HOME/.config"}/pkgfoster.keep"
if [ ! -f "$CONF" ]; then
mkdir -p "${XDG_CONFIG_HOME}"
touch "$CONF"
fi
echo "Checking packages for orphans..."
while true ; do
RECHECK=0
orphans=$(comm -13 <(cat <(find "$BASE" -maxdepth 1 -type d -printf "%f\n") \
<(for (( k=0; k<${#FOSTERED[@]}; k++ )); do echo "${FOSTERED[$k]}"; done) \
| sort -u) <($PRT_GET listorphans | sort) | tr '\n' ' ')
orphans=$(comm -23 <($PRT_GET listorphans | sort) \
<(cat <(find "$BASE" -maxdepth 1 -type d -printf "%f\n") "$CONF" \
| sort -u) | tr '\n' ' ')
for PACKAGE in ${orphans[@]}; do
echo
@ -50,16 +53,11 @@ while true ; do
$PKGRM $PACKAGE
RECHECK=1
else
FOSTERED=( $PACKAGE ${FOSTERED[@]} )
echo $PACKAGE >> "$CONF"
fi
done
if [ "$RECHECK" = "0" ] ; then
echo -n "FOSTERED=(" > $CONF
for (( k=0; k<${#FOSTERED[@]}; k++ )); do
echo -n "${FOSTERED[$k]} " >> $CONF
done
echo ")" >> $CONF
exit 0
fi

View File

@ -19,15 +19,34 @@ our %options = ( auto => 0, dryrun => 0, rmdir => 0, pkgtoo => 0, quiet => 0 );
our @portdirs;
our $argports;
######################### main routine ################################
parse_args();
print_usage() if ((2*$argports-1)*(1-2*$options{auto}) < 0);
if ($options{auto} == 1) {
my @basedirs = getportdirs();
foreach my $collection (@basedirs) {
print "====> Sweeping port collection $collection\n";
foreach my $port (list_subdirs($collection)) {
do_sweep($port);
}
}
} else {
foreach my $port (@portdirs) {
do_sweep($port);
}
}
######################### subroutines #################################
sub print_usage {
print <<EOT;
Usage: prtsweep [OPTION]... [PORTDIRS]...
-a automatic mode, only valid when [PORTDIRS] are omitted
-d remove directories of dropped ports (no Pkgfile present)
-d remove directories of dropped ports (signature file not found)
-n dry-run, don't actually delete anything
-p delete any built packages too
-q quiet mode
-q quiet mode, only print messages when files are removed
Report bugs on libera.chat #crux-devel
EOT
@ -36,22 +55,24 @@ EOT
sub parse_args {
foreach my $arg (@ARGV) {
next if (-f $arg); # no filenames, only options or directories
if ($arg =~ /^-a$/) {
if ($arg eq "-a") {
$options{auto} = 1;
} elsif ($arg =~ /^-d$/) {
} elsif ($arg eq "-d") {
$options{rmdir} = 1;
} elsif ($arg =~ /^-n$/) {
} elsif ($arg eq "-n") {
$options{dryrun} = 1;
} elsif ($arg =~ /^-p$/) {
} elsif ($arg eq "-p") {
$options{pkgtoo} = 1;
} elsif ($arg =~ /^-q$/) {
} elsif ($arg eq "-q") {
$options{quiet} = 1;
} elsif ($arg =~ /^--version$/) {
} elsif ($arg eq "--version") {
print $version."\n";
exit 0;
} elsif (-d "$arg") {
} elsif ((-d "$arg") || # false for symlink to a portdir,
(-f "$arg/.signature")) { # so a second test is needed
push (@portdirs, $arg);
} elsif (-f "$arg") {
print "WARN: $arg is not a port directory or recognized option, ignoring.\n";
} else {
print_usage();
}
@ -68,7 +89,8 @@ sub list_subdirs {
opendir(DIR, $path) or return;
foreach my $entry(sort(readdir(DIR))) {
next if ( substr($entry,0,1) eq '.' );
push (@list, "$path/$entry") if -d "$path/$entry";
push (@list, "$path/$entry") if ((-d "$path/$entry")
or (-f "$path/$entry/.signature"));
}
closedir(DIR);
return @list;
@ -97,15 +119,15 @@ sub sweep {
print "=======> $port\n" unless $options{quiet}==1;
my @wanted = parse_signature ("$port/.signature");
my $builtpkg=($options{pkgtoo} != 1) ? $path[-1].'#.*pkg\.tar.*' :
'ReallyLongStringThatWouldNeverMatchAnyBuiltPackage';
$builtpkg =~ s/\+/\\\+/;
my $builtpkg=$path[-1].'#.*pkg\.tar\.(bz2|gz|lz|xz)$';
$builtpkg =~ s/\+/\\\+/; # plus sign in filenames interferes with regex search
opendir (DIR, $port) or return;
foreach my $f (sort(readdir(DIR))) {
next if ( $f eq '.' or $f eq '..' );
$f =~ s/\+/\\\+/; # plus sign in filenames interferes with regex search
if ((grep /$f/, @wanted) >= 1 or ($f =~ /$builtpkg/)) {
$f =~ s/\+/\\\+/;
if ((grep /$f/, @wanted) >= 1 or
($f =~ /$builtpkg/)*($options{pkgtoo}==0)) {
print "... keeping file $port/$f.\n" unless $options{quiet} == 1;
} else {
remove ("$port/$f");
@ -127,15 +149,17 @@ sub remove {
}
sub do_sweep {
# argument either a real directory (not symlink) or has a signature;
# this subroutine determines which condition was satisfied.
my $port = shift; my $nf = 0;
if (! -f "$port/.signature") {
opendir (PORTDIR,$port) or die "cannot open $port for reading\n";
opendir (PORTDIR,$port) or return;
foreach my $f (readdir PORTDIR) {
next if ($f eq '.' or $f eq '..');
$nf += 1;
}
closedir (PORTDIR);
print "no signature file, $port does not define a valid port.";
print "WARN: $port/.signature not found, invalid port directory.";
rm_emptydir($port,$nf);
} else {
sweep($port);
@ -145,10 +169,11 @@ sub do_sweep {
sub rm_emptydir {
my $port = shift; my $nf = shift;
my $msg = ($options{rmdir}==1) ? "\n":
" use -d to remove empty directories.\n";
"\n Use -d to remove empty directories.\n";
my $modal = ($options{dryrun}==0) ? "" : "would be";
$msg = ($nf == 0) ? "$msg empty directory $port $modal deleted.\n" :
"$msg cannot remove $port: directory not empty\n";
my $post = ($nf == 0) ? " Empty directory $port $modal deleted.\n" :
" Cannot remove $port: directory not empty\n";
$msg = ($options{rmdir}==1) ? "$msg $post" : $msg ;
print $msg;
rmdir ($port) if (($nf == 0) and ($options{dryrun} == 0));
}
@ -179,21 +204,3 @@ sub getportdirs {
closedir PORTS_DEFS;
return @basedirs ;
}
# main
parse_args();
print_usage() if ((2*$argports-1)*(1-2*$options{auto}) < 0);
if ($options{auto} == 1) {
my @basedirs = getportdirs();
foreach my $collection (@basedirs) {
print "====> Sweeping port collection $collection\n";
foreach my $port (list_subdirs($collection)) {
do_sweep($port);
}
}
} else {
foreach my $port (@portdirs) {
do_sweep($port);
}
}

View File

@ -18,7 +18,7 @@ our @portdirs;
our $argports;
my $version = 1.2.1;
our %options = ( oldver=>0, pkgtoo=>0, srctoo=>0, addons=>0,
dryrun=>0, auto=>0, quiet=>0 );
dryrun=>0, auto=>0, quiet=>0, parse_pkgmk=>0 );
#################### main routine ################################
parse_args();
@ -46,9 +46,8 @@ if ($options{auto} == 1) {
#################### subroutines #################################
sub parse_args {
while ( my $arg = pop(@ARGV) ) {
next if (-f $arg); # no filenames, only directories and options
if ($arg eq "-b") {
foreach my $arg (@ARGV) {
if ($arg eq "-b") {
$options{oldver} = 1;
} elsif ($arg eq "-p") {
$options{pkgtoo} = 1;
@ -62,11 +61,16 @@ sub parse_args {
$options{auto} = 1;
} elsif ($arg eq "-q") {
$options{quiet} = 1;
} elsif ($arg eq "--parse-pkgmk-conf") {
$options{parse_pkgmk} = 1;
} elsif ($arg eq "-v") {
print "$version\n";
exit(0);
} elsif (-d $arg) {
} elsif ((-d $arg) || # false for symlink to a portdir, so a
(-f "$arg/Pkgfile")) { # followup test is performed to catch those
push (@portdirs, $arg);
} elsif (-f $arg) {
print "WARN: ignoring invalid port directory $arg\n";
} else {
print_usage();
}
@ -124,7 +128,7 @@ sub parse_pkgfile {
return \$name, \$version, \$release, \@source, \@renames;
}
sub keeplist { # remember to pop off the last element for regex purposes
sub keeplist { # remember to pop off the last two elements for regex purposes
my $port = shift;
my @keepers = ("Pkgfile",".footprint",".signature");
push (@keepers,"pre-install","post-install","README","README.md",
@ -140,7 +144,7 @@ sub keeplist { # remember to pop off the last element for regex purposes
my $i; my $si; my $ki;
ENTRY: for ($i=0; $i<=$#source; $i++) {
$si = $source[$i];
$ki = ($renames[$i] eq "SKIP" or $renames[$i] eq "") ? basename($si) :
$ki = ("$renames[$i]" eq "SKIP" or "$renames[$i]" eq "") ? basename($si) :
$renames[$i];
if ($si =~ /^remote:/) {
next ENTRY if ($options{srctoo}==1);
@ -150,21 +154,23 @@ sub keeplist { # remember to pop off the last element for regex purposes
push(@keepers, $ki);
}
}
push (@keepers, $name.'#'.$version.'-'.$release.".pkg.tar.$compression_mode") if $options{pkgtoo}==0;
push (@keepers, $name."#.*pkg.tar.$compression_mode");
push (@keepers, "$name#$version.*\.pkg\.tar\.$compression_mode");
push (@keepers, "$name#.*\.pkg\.tar\.$compression_mode");
return @keepers;
}
sub get_compress {
my $conf = "/etc/pkgmk.conf";
$compression_mode = "gz";
open(CONFIG,$conf) or return $compression_mode;
while(<CONFIG>) {
$compression_mode = $1 if m/^[\t ]*PKGMK_COMPRESSION_MODE=(.*)\n/;
my $suffix = "(gz|lz|xz|bz2)";
if ($options{parse_pkgmk} == 1) {
my $conf = "/etc/pkgmk.conf";
open(CONFIG,$conf) or return $suffix;
while(<CONFIG>) {
$suffix = $1 if m/^[\t ]*PKGMK_COMPRESSION_MODE=(.*)\n/;
}
close(CONFIG);
$suffix =~ s/"//g; # ensure no double-quotes remain (thanks jaeger)
}
close(CONFIG);
$compression_mode =~ s/"//g; # ensure no double-quotes remain (thanks jaeger)
return $compression_mode;
return $suffix;
}
sub do_wash {
@ -174,16 +180,18 @@ sub do_wash {
print "WARN: no Pkgfile found in $port. Skipping.\n";
return;
} else {
my @keepers = keeplist($port);
my $nevermatch = "ReallyLongStringThatWouldNeverMatchAnyBuiltPackage";
my $pkgrex = pop(@keepers);
$pkgrex = ($options{oldver} == 1 or $options{pkgtoo} == 1) ? $nevermatch : $pkgrex;
my @keepers = keeplist($port); my $iswanted;
my $allbuilds = pop(@keepers);
my $currbuild = pop(@keepers);
opendir (DIR,$port) or return;
print "=====> washing $port\n" unless $options{quiet} == 1;
foreach my $f (sort(readdir(DIR))) {
next if ($f eq '.' or $f eq '..');
$f =~ s/\+/\\\+/g; # plus sign in filenames interferes with regex search
if ((grep /$f/, @keepers) or ($f =~ /$pkgrex/)) {
$iswanted = ( grep (/$f/, @keepers ) >= 1 );
if ($iswanted or ($options{pkgtoo}==0)*($f =~ /$currbuild/)
or ($options{oldver}==0)*($f =~ /$allbuilds/)*($f !~ /$currbuild/)) {
print "... keeping file $port/$f.\n" unless $options{quiet} == 1;
} else {
remove ("$port/$f");
@ -194,8 +202,7 @@ sub do_wash {
}
sub getportdirs { # returns scalar references to two arrays
my @basedirs;
my @localports;
my @basedirs; my @localports;
my $conf = "/etc/prt-get.conf";
open(PORTS, $conf) or die "could not open $conf";
@ -211,7 +218,7 @@ sub getportdirs { # returns scalar references to two arrays
push @basedirs, $line if (-d $line);
} else {
my @a = split(/:/, $line);
my @b = split(/,/, @a[1]);
my @b = split(/,/, $a[1]);
while ( my $c = pop @b ) {
my $port = $a[0] . "/" . $c;
push @localports, $port if (-d $port);