#!/bin/bash # Simple tool to update and install AUR packages for Arch Linux # Written by Nathaniel Maia, September 2018 # This software is provided free of charge and WITHOUT warranty declare -g NOVIEW="" # disable viewing the PKGBUILD declare -g NOCONF="" # disable extra confirmations declare -i CACHE=0 # keep cached package downloads readonly BUILD_DIR="$HOME/.cache/aur_builds" # AUR build directory readonly AUR_ADDR="https://aur.archlinux.org" # AUR web address readonly PKG_ADDR="$AUR_ADDR/packages" # AUR package address ## ----------------------------------------------------------------- ## readonly SCRIPT="$(readlink -f "$0")" # real path to this file declare -a AUR_PKGS=() # array of AUR packages declare -a PAC_PKGS=() # array of official packages usage() { # show the standard UNIX style help message.. usage: usage cat <<- EOF baph - Basic AUR Package Helper, a simple script to install and update packages usage: baph [package(s)] [options] operations: baph {-h --help} baph {-u --update} [package(s)] [options] baph {-i --install} [options] options: --noview Skip viewing the PKGBUILD for AUR packages and dependencies --noconfirm Skip most confirmations when building also passed to pacman EOF } #-- Utility functions --# msg() { printf "$1\e[0m \e[1m$2\e[0m\n"; } die() { printf "\e[1m\e[31merror:\e[0m $1\n"; exit ${2:-1}; } comp() { # compare AUR package versions, returns 0 or 1.. usage: comp "package" "current version" NEW="$(curl --silent "$PKG_ADDR/$1" | awk '/Details:/ {sub(/<\/h.?>/, ""); print $4}')" [[ -n $NEW && "$2" == "$NEW" ]] && return 0 || return 1 } yesno() { # only shown when not using --noconfirm, returns 0 or 1.. usage: yesno "question" if [[ $NOCONF ]]; then printf "\n\e[34m::\e[0m \e[1m$1? [Y/n]\e[0m "; read -r conf [[ $conf =~ [nN] ]] && return 1 # return non zero when confirm n/N fi return 0 } #-- Update functions --# prepupdate() { # prep for package update.. usage: prepupdate (( ${#AUR_PKGS[@]} == 0 )) || update # exits here if passed packages to update sudo pacman -Syyu $NOCONF || exit 1 AUR_PKGS=($(pacman -Qqm 2>/dev/null)) (( ${#AUR_PKGS[@]} == 0 )) && msg '\e[34m::' "No AUR packages installed.." || update } update() { # check update for each package in AUR_PKGS=().. usage: update local cur local up=() msg '\e[34m::' "Synchronizing AUR package versions..." for i in "${AUR_PKGS[@]}"; do ver="$(pacman -Qs "^$i$" 2>/dev/null | awk 'NR==1 {print $2}')" if [[ $ver ]]; then comp "$i" "$ver" && printf "$i-$ver --> up to date\n" || up+=("$i") else msg '\e[33m::' "$i is not installed.. skipping" fi done msg '\e[34m::' "Starting AUR package upgrade..." if (( ${#up[@]} > 0 )); then printf "\n\e[1mPackages (${#up[@]})\e[0m %s\n\n" "${up[@]}" for i in "${up[@]}"; do getpkg "$i" done else printf " there is nothing to do\n" fi exit 0 } #-- Install functions --# prepinstall() { # loop package arrays if set and install each.. usage: prepinstall (( ${#PAC_PKGS[@]} == 0 && ${#AUR_PKGS[@]} == 0 )) && die "no targets specified" 2 (( ${#PAC_PKGS[@]} > 0 )) && sudo pacman -S ${PAC_PKGS[*]} $NOCONF for pkg in "${AUR_PKGS[@]}"; do response="$(curl --silent --head "$PKG_ADDR/$pkg" | awk 'NR==1 {print $2}')" (( response == 200 )) || die "$response response from $PKG_ADDR/$pkg" getpkg "$pkg" || msg '\e[33m::' "Exiting $pkg build early" done } getpkg() { # install an AUR package.. usage: getpkg "package" local bd="$BUILD_DIR/$1" local pkgbuild="$bd/PKGBUILD" comp "$1" && msg '\e[1m'$'\e[33mwarning:' "$1-$OLD is up to date -- reinstalling" (( CACHE == 1 )) || rm -rf "$bd" >/dev/null 2>&1 [[ -d $bd ]] || mkdir -p "$bd" if [[ ! -e $pkgbuild || $CACHE -eq 0 ]]; then git clone "$AUR_ADDR/$1" "$bd" || die "failed to clone $AUR_ADDR/$1" elif [[ -e $pkgbuild ]]; then cd "$bd" git pull || die "failed to pull from $AUR_ADDR/$1" fi [[ -e $pkgbuild ]] && cd "$bd" || die "$bd doesn't contain a PKGBUILD" view "$1" "$pkgbuild" && { yesno "Continue building $1" || return 1; } buildpkg "$1" "$pkgbuild" return 0 } view() { # view PKGBUILD.. usage: view "package" "/path/to/PKGBUILD" if ! [[ $NOVIEW ]]; then yesno "View the PKGBUILD for $1" || return 1 if hash nvim >/dev/null 2>&1; then nvim -n -R -u "$(find /usr/share/nvim/ -wholename '*/macros/less.vim')" "$2" elif hash vim >/dev/null 2>&1; then vim -n -R -u "$(find /usr/share/vim/ -wholename '*/macros/less.vim')" "$2" else less -P " -- Viewing PKGBUILD for $1 -- (press q to exit)" "$2" fi fi } buildpkg() { # build package.. usage: buildpkg "package" "/path/to/PKGBUILD" eval "$(cat "$BUILD_DIR/$1/PKGBUILD")" (( ${#validpgpkeys[@]} > 0 )) && importkeys ${validpgpkeys[*]} (( ${#depends[@]} > 0 )) && builddeps "$1" ${depends[*]} makepkg -sicr } importkeys() { # import PGP keys from package.. usage: importkeys ${KEYS[@]} for key in "$@"; do pacman-key --list-keys | grep -q "$key" && continue msg '\e[33m::' "resolving missing key $key" gpg --receive-keys "$key" sudo pacman-key -r "$key" && sudo pacman-key --lsign-key "$key" done } builddeps() { # build package depends.. usage: builddeps "package" ${DEPENDS[@]} local pdir="$1"; shift for i in "$@"; do depend="$(sed 's/[=<>]=\?[0-9]*\?\.\?[0-9]*\?\.\?[0-9]*\?//g' <<< "$i")" available="$(pacman -Ssq "^$depend$" 2>/dev/null)" [[ $available ]] && continue msg '\e[33m::' "resolving AUR dependency -> $depend" getpkg "$depend" || die "failed to build dependency $depend" done cd "$BUILD_DIR/$pdir" } #-- Argument parsing & execution --# pkg_args() { # parse additional command line arguments.. usage: pkg_args $@ for i in "$@"; do case $i in --noview) NOVIEW="--noview" ;; --noconfirm) NOCONF="--noconfirm" ;; --*|-[a-z]) die "invalid option -- '$i'" ;; *) pacman -Ssq "^$i$" >/dev/null 2>&1 && PAC_PKGS+=("$i") || AUR_PKGS+=("$i") esac done } # catch Ctrl-C and exit trap 'die "^C caught.."' SIGINT if (( $# == 0 )); then die "no operation specified (use -h for help)" 2 elif ! hash pacman git curl >/dev/null 2>&1; then die "this script requires the following packages installed: pacman git curl" else for arg in "$@"; do case $arg in --help|-h) usage && exit 0 ;; --update|-u) shift; pkg_args "$@"; prepupdate; break ;; --install|-i) shift; pkg_args "$@"; prepinstall; break ;; *) die "invalid option -- '$arg'" esac done [[ $CACHE -eq 0 && -d $BUILD_DIR ]] && rm -rf "$BUILD_DIR" >/dev/null 2>&1 fi