#!/usr/bin/env bash set -eE # Copyright (C) 2011-2012 Nicolás Reynolds # Copyright (C) 2012-2013, 2015, 2017-2018 Luke Shumaker # # If you don't see m4_include(...) below, but see function definitions # for msg() et al., then this is a generated file, and contains some # code from librelib. See the source distribution for full copyright # information. # # License: GNU GPLv3+ # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Performs chroot cleanup smartly, it only removes the unneeded packages or # leaves you with a clean system # Library routines ############################################################# # Statically include various library routines to avoid having # dependencies on outside files. export TEXTDOMAIN='libretools' export TEXTDOMAINDIR='/usr/share/locale' if type gettext &>/dev/null; then _() { gettext "$@"; } else _() { echo "$@"; } fi # Begin chcleanup.lib ########################################################## m4_include(chcleanup.lib) # End chcleanup.lib ############################################################ # User interface ############################################################### DRYRUN=${DRYRUN:-false} if [[ ! -f /.arch-chroot ]] && ! ${DRYRUN}; then error "(chcleanup): Must be run inside of a chroot" exit 1 fi # Load configuration ########################################################### CHROOTPKG=(base-devel) # Note: the in-chroot pkgconfdir is non-configurable, this is # intentionally hard-coded. source /etc/libretools.d/chroot.conf # If we're running makepkg if [[ -f ./PKGBUILD ]]; then if [[ ! -f ./.SRCINFO || ./PKGBUILD -nt ./.SRCINFO ]]; then sudo -u "#$(stat -c %u -- ./PKGBUILD)" sh -c 'makepkg --printsrcinfo > .SRCINFO' fi CARCH="$(. /etc/makepkg.conf; printf '%s' "$CARCH")" mapfile -t DEPENDS < <(sed -nE "s/^\\s+(|make|check)depends(|_${CARCH}) = //p" < .SRCINFO) else DEPENDS=() fi # Main ######################################################################### msg "Cleaning chroot..." # Sync the local repo with pacman (a limited form of `pacman -Sy`) cp /repo/repo.db /var/lib/pacman/sync/repo.db # Setup the temporary directory TEMPDIR="$(mktemp --tmpdir -d "${0##*/}.XXXXXXXXXX")" trap "rm -rf -- ${TEMPDIR@Q}" EXIT # Set up a scratch pacman DB mkdir -- "$TEMPDIR/db" "$TEMPDIR/db/local" "$TEMPDIR/hooks" cp -a -t "${TEMPDIR}/db" -- /var/lib/pacman/sync { echo /usr/share/libalpm/hooks; pacman-conf HookDir; } | while read -r dir; do for hook in "$dir"/*.hook; do ln -sfT -- /dev/null "$TEMPDIR/hooks/${hook##*/}" done done pacman=(pacman --dbpath="$TEMPDIR/db" --hookdir="$TEMPDIR/hooks") # Do our best to preload the scratch DB with CHROOTPKG and # CHROOTEXTRAPKG packages. This is purely an optimization step. The # safety of this optimization assumes that none of CHROOTPKG, # CHROOTEXTRAPKG, *or their dependancies* are virtual packages. We # don't include DEPENDS in this optimization, because this assumption # doesn't hold for them. while read -r pkg; do if [[ -d /var/lib/pacman/local/$pkg ]]; then cp -a -T -- "/var/lib/pacman/local/$pkg" "$TEMPDIR/db/local/$pkg" fi done < <("${pacman[@]}" -Sp --print-format='%n-%v' -- "${CHROOTPKG[@]}" "${CHROOTEXTRAPKG[@]}") # Get the full list of packages needed by dependencies, including the base system msg2 "Creating a full list of packages..." for var in CHROOTPKG CHROOTEXTRAPKG DEPENDS; do declare -n pkgsref="$var" if [[ $var = DEPENDS ]]; then mapfile -t pkgs < <("${pacman[@]}" -T -- "${pkgsref[@]}") else pkgs=("${pkgsref[@]}") fi if (( ${#pkgs[@]} == 0 )); then continue fi "${pacman[@]}" -S --dbonly --noscriptlet --needed --noconfirm -- "${pkgs[@]}" <&- >& "$TEMPDIR/pacman.txt" || ret=$? if (( ret != 0 )); then error "Could not create a full list of packages, exiting." plain "This is likely caused by a dependency that could not be found." sed 's/^/ > /' <"$TEMPDIR/pacman.txt" >&2 exit $ret fi done "${pacman[@]}" -Qq >"$TEMPDIR/pkglist.txt" # Diff installed packages against a clean chroot then remove leftovers packages=($(comm -23 <(pacman -Qq | sort -u) \ <(sort -u "$TEMPDIR/pkglist.txt"))) if [[ ${#packages[@]} = 0 ]]; then msg2 "No packages to remove" else msg2 "Removing %d packages" ${#packages[@]} if ${DRYRUN}; then echo "${packages[*]}" else # Only remove leftovers, -Rcs removes too much pacman --noconfirm -R --nosave "${packages[@]}" fi fi packages=($(comm -13 <(pacman -Qq | sort -u) \ <(sort -u "$TEMPDIR/pkglist.txt"))) if [[ ${#packages[@]} = 0 ]]; then msg2 "No packages to add" else msg2 "Adding %d packages" ${#packages[@]} if ${DRYRUN}; then echo "${packages[*]}" else pacman --noconfirm -S "${packages[@]}" fi fi