5 # aurget: A simple pacman-like interface to the AUR
7 # http://pbrisbin.com/posts/aurget/
13 SCRIPT_DESCRIPTION
='A simple pacman-like interface to the AUR'
14 SCRIPT_AUTHOR
='Patrick Brisbin <pbrisbin@gmail.com>'
15 SCRIPT_VERSION
='3.4.0'
16 SCRIPT_DATE
='11/9/2011'
20 # script utilities {{{
21 display
() { echo -e "${colorB}:: ${colorW}$*${nocolor}"; }
23 warn() { echo -e "${colorY}warning: ${nocolor}$*" >&2; }
26 echo -e "${colorR}error: ${nocolor}$*" >&2
28 [[ -d "$tmpdir" ]] && rm -r "$tmpdir"
36 Lock file found at $lock_file.
38 Another aurget process is currently running. Please wait until it has
46 local pkg
="$1" src
="$2" dep
="$3" msg
48 msg
="notice: $pkg failed while building, remove source files ($src)"
50 # $src must be nonzero so we don't attempt to rm -rf /*
51 prompt
"$msg" && [[ -n "$src" ]] && rm -rf "$src/"*
53 # if it's a dep we need to errorout entirely
54 if [[ $dep -eq 1 ]]; then
55 errorout
"dependency package $pkg failed to build. unable to continue."
57 # otherwise, warn but continue
59 warn
"package $pkg failed to build and won't be installed."
63 # prompt and return based on choice
83 # uses eval to process --option=value type arguments
84 runtime_flag
() { eval ${1/--/}; }
86 # check for development
87 is_devel
() { echo "$1" |
grep -q -- "$development_regex"; }
91 [[ -d "$tmpdir" ]] && rm -r "$tmpdir"
100 usage: aurget [ -v | -h | -S* [ --options ] [ -- ] <arguments> ]
103 -S <package> process <package> using your default sync_mode
105 -Sd <package> download <package>
106 -Sb <package> download and build <package>
107 -Sy <package> download, build, and install <package>
109 -Su process available upgrades using your default sync_mode
111 -Sdu download available upgrades
112 -Sbu download and build available upgrades
113 -Syu download, build, and install available upgrades
115 -Ss <term> search aur for <term>
116 -Ssq <term> search aur for <term>, print only package names
117 -Sp <package> print the PKGBUILD for <package>
118 -Si <package> print extended info for <package>
120 --rebuild always rebuild (ignore your cache)
122 --devel only affects -Su, add all development packages
124 --deps resolve dependencies
125 --nodeps don't resolve dependencies
127 --edit prompt to edit all pkgbuilds
128 --noedit don't prompt to edit any pkgbuilds
130 --discard discard source files after building
131 --nodiscard don't discard source files after building
133 --nocolor disable colorized output
134 --noconfirm auto-answer y to all prompts
136 --ignore '<package> <package> <...>'
137 add additional packages to be ignored
139 --mopt '-opt -opt ...'
140 add additional options to the build command
142 --popt '-opt -opt ...'
143 add additional options to the install command
145 -v, --version display version info
146 -h, --help display this
148 --option=value set config <option> as <value> for this run only
161 $SCRIPT_NAME: $SCRIPT_DESCRIPTION
163 author: $SCRIPT_AUTHOR
164 version: $SCRIPT_VERSION
165 last update: $SCRIPT_DATE
178 local pkgbuild reason
181 reason
="${*:-sourcing for deps}"
183 [[ ! -f "$pkgbuild" ]] && errorout
"$Name: no PKGBUILD found"
185 # if we prompted for sourcing, don't prompt again afterwards
186 for package
in "${prompted[@]}"; do
187 if [[ $package == $Name ]]; then
192 prompted
=($prompted $Name)
194 # default is to prompt
195 case "${edit_pkgbuilds:-prompt}" in
197 prompt
) prompt
"Edit PKGBUILD before $reason" ||
return 0 ;;
200 $editor "$pkgbuild" || errorout
'problem editing PKGBUILD'
201 prompt
"Continue $reason"
206 # source_pkgbuild {{{
210 [[ ! -f "$pkgbuild" ]] && errorout
'pkgbuild not found'
212 # source pkgbuild variables
213 source <(awk '/^build.*(.*)|^function.*build.*(.*)/{exit};1' "$pkgbuild")
220 # we never discard sources
221 $discard_sources ||
return
223 # we keep devels and this is a devel package
224 $dont_discard_devels && is_devel
${1##*/} && return
226 # use find -delete incase the built package is still in the directory
227 #display "Discarding sources..."
228 #find "$1" -depth ! -name "${1##*/}*$pkgext" -delete 2>/dev/null
230 # turns out, the built package is _always_ in the directory
231 display
'Discarding sources...'
239 local multiple
=false ret
=0 url
242 [[ ${#args[@]} -eq 0 ]] && clean_exit
1
244 # more than one means print names and whitespace
245 [[ ${#args[@]} -gt 1 ]] && multiple
=true
247 for name
in "${args[@]}"; do
248 $multiple && display
"$name/PKGBUILD ==>"
250 url
="$aur_pkgs/$(url_encode "${name:0:2}/$name")/PKGBUILD"
251 curl
-fsS "$url" 2>/dev
/null || ret
=1
264 local string
="$1" awkscript
267 $1 == "20" { printf("%s", "+"); next } # space becomes plus
268 $1 ~ /0[adAD]/ { next } # strip newlines
269 $2 ~ /^[a-zA-Z0-9.*()\/-]$/ { printf("%s", $2); next } # pass through what we can
270 { printf("%%%s", $1) } # take hex value of everything else
273 # 6/19/2010 + thanks to Irm for this bit of magic
274 hexdump -v -e '1/1 "%02x\t"' -e '1/1 "%_c\n"' <<< "$string" | LANG
=C
awk "$awkscript"
281 # just print pkgbuild?
282 [[ "$search_mode" = 'print' ]] && print_pkgbuild
"${args[@]}"
284 # continue with other searches
285 local awkscript results term Name Version Description URL URLPath OutOfDate
290 /Name":/ { printf "%s", $3 }
291 /Version":/ { printf "|%s", $3 }
292 /Description":/ { printf "|%s", $3 }
293 /URL":/ { printf "|%s", $3 }
294 /OutOfDate":/ { printf "|%s", $3 }
295 /URLPath":/ { printf "|%s\n", $3 }
299 results
="$(curl -fsS "${aur_srch}$
(url_encode
"$term")" | awk -F'"' -v RS='","' "$awkscript")"
302 [[ -z "$results" ]] && clean_exit 1
304 # sed-parse that file
305 while IFS='|
' read -r Name Version Description URL OutOfDate URLPath; do
306 [[ -z "$Name" ]] && continue
307 [[ "$search_mode" = 'info
' ]] && [[ "$Name" != "$term" ]] && continue
309 # FYI: http://old.nabble.com/echo-interrupted-by-SIGCHLD-from-a-dying-coprocess-td28028050.html
310 case "$search_mode" in
311 quiet) echo "$Name" 2>/dev/null ;;
313 info) [[ "$OutOfDate" =~ ^(1|OutOfDate)$ ]] && outofdate="${colorR}Yes${nocolor}" || outofdate='No
'
314 echo -e "${colorW}Repository \t: ${colorM}aur${nocolor}" 2>/dev/null
315 echo -e "${colorW}Name \t\t: $Name${nocolor}" 2>/dev/null
316 echo -e "${colorW}Version \t: ${colorG}$Version${nocolor}" 2>/dev/null
317 echo -e "${colorW}URL \t\t: ${colorC}${URL//\\\///}${nocolor}" 2>/dev/null
318 echo -e "${colorW}Out of date \t: ${nocolor}$outofdate" 2>/dev/null
319 echo -e "${colorW}Description \t: ${nocolor}${Description//\\\///}\n" 2>/dev/null ;;
321 search) [[ "$OutOfDate" =~ ^(1|OutOfDate)$ ]] && outofdate="${colorR}[out of date]${nocolor}" || outofdate=''
322 echo -en "${colorM}aur/${colorW}$Name" 2>/dev/null
323 echo -en " ${colorG}$Version${nocolor}" 2>/dev/null
324 echo -e " $outofdate" 2>/dev/null
325 echo -e " ${Description//\\\///}" 2>/dev/null ;;
327 *) errorout 'invalid search_mode
'
329 done < <(sort <<< "$results")
338 display 'Starting AUR upgrade...
'
339 while read -r name vers; do
340 if is_devel $name; then
341 $devels && args+=("$name")
344 versN="$(curl -fsS "${aur_info}$(url_encode "$name")" | sed -e '/.
*"Version":"\([^"]*\
)".*/!d; s//\1/g')"
345 [[ -z "$versN" ]] && continue
347 # check against ignores
348 if echo " $ignore_packages " |
grep -Fq -- " $name "; then
349 warn
"$name: ignoring package upgrade ($vers => $versN)"
354 check
=$
(vercmp
"$vers" "$versN")
355 [[ $check -gt 0 ]] && warn
"$name: local ($vers) is newer than aur ($versN)"
356 [[ $check -lt 0 ]] && args
+=("$name")
360 [[ ${#args[@]} -ne 0 ]] && setup_targets ||
echo ' there is nothing to do'
367 local name
="$1" version
="$2"
370 pkg
="$package_directory/$name-$version-${CARCH}$pkgext"
372 if [[ ! -f "$pkg" ]]; then
374 pkg
="$package_directory/$name-$version-any$pkgext"
377 if [[ ! -f "$pkg" ]]; then
387 # download_tarball {{{
389 local name
="$1" url
="$2" target
391 display
'Retrieving source tarball from AUR...'
392 target
="$build_directory/$name.tar.gz"
393 url
="${aur}$(url_encode "${url//\\\///}")"
394 curl
-f#o "$target" "$url" || errorout 'failed to retrieve aur sources'
396 # JSON often reports a url, it'll download but won't be a tarball
397 if ! grep -Fq 'gzip' <(file "$target"); then
398 warn
"$name: not a valid tarball, trying alternate url..."
399 curl
-f#o "$target" "$aur_pkgs/$(url_encode "${name:0:2}/$name/$name").tar.gz" || errorout 'failed to retrieve aur sources'
402 # stop here if that's how we roll
403 [[ "$sync_mode" = 'download' ]] && clean_exit
406 display
'Extracting source tarball...'
407 (cd "$build_directory" && tar xvzf
"$target" &>/dev
/null
) || errorout
"$build_directory/$name.tar.gz: failed unpacking the tarball"
409 # remove the tarball post extract
412 # specify sub-sourcedir
413 _srcdir
="$build_directory/$name"
420 local dep
="$1" pkgbuild
422 display
'Building package...'
423 pushd "$_srcdir" &>/dev
/null || errorout
'failed to change directory'
425 if [[ -f "/etc/customizepkg.d/$Name" ]]; then
426 display
'Calling customizepkg...'
427 customizepkg
--modify || warn
'customizepkg had a problem'
430 if ! prompt_to_edit
'PKGBUILD' "building $Name"; then
432 try_discard
"$_srcdir"
436 # if we fail building, errorout on dep or just return 1 on
438 if ! PKGDEST
="$package_directory" $makepkg $mopts; then
439 builderror
"$Name" "$_srcdir" "$dep"
445 # this may or may not discard things
446 try_discard
"$_srcdir"
448 # stop here if that's how we roll
449 [[ "$sync_mode" = 'build' ]] && clean_exit
451 # look for package and any package by name
452 pkg
="$(find_pkg "$Name" "$Version")"
455 if [[ -z "$pkg" ]]; then
456 # don't build two dev packages of the same name within the same
457 # minute; this is the only way to find git/svn/etc packages' exact
459 pkg
="$(find "$package_directory" -ctime -1 -name "$Name-*$pkgext" -print -quit)"
467 # install_package {{{
469 local pkg
="$1" dep
="$2"
471 display
'Installing package...'
473 if [[ -f "$pkg" ]]; then
474 if [[ $dep -eq 1 ]]; then
475 $pacman --asdep $popts "$pkg" || errorout
'failed installing the package'
477 $pacman $popts "$pkg" || errorout
'failed installing the package'
480 errorout
"$pkg: package not found"
486 # source_for_deps {{{
488 local pkgbuild
="$tmpdir/${1}_PKGBUILD"
490 # we've already checked this
491 [[ -f "$pkgbuild" ]] && continue
493 # if this errors, we know it's not aur-available so return 1
494 if ! curl
-fso "$pkgbuild" "$aur/packages/$(url_encode "${1:0:2}/$1")/PKGBUILD"; then
499 Name
="$1" prompt_to_edit
"$pkgbuild" "sourcing $1 for dependencies" && source_pkgbuild
"$pkgbuild" ||
continue
506 echo 'resolving dependencies...'
508 local dep all_deps pac_deps
509 all_deps
=(); aur_deps
=(); pac_deps
=() mk_deps
=()
514 for name
in "${args[@]}" "${all_deps[@]}"; do
515 # keep args out of deps so error messages are more sane
516 if ! source_for_deps
"$name"; then
517 # assume it's a pacman dep for now
518 grep -Fq -- " $name " <<< " ${args[@]} " || pac_deps
+=( "$name" )
520 grep -Fq -- " $name " <<< " ${args[@]} " || aur_deps
=( "$name" "${aur_deps[@]}" )
522 # filter satasfied deps, clear versioning
523 depends
=( $
(pacman
-T -- ${depends[@]} ${makedepends[@]}) )
524 depends
=( "${depends[@]%>*}" )
525 depends
=( "${depends[@]%=*}" )
526 depends
=( "${depends[@]%<*}" )
528 # store make depends for later removal
529 mk_deps
+=( "${makedepends[@]}" )
531 # track unsatisfied deps
532 [[ ${#depends[@]} -ne 0 ]] && all_deps
+=( "${depends[@]}" )
536 # if we found no more deps this round, we can stop
537 [[ ${#all_deps[@]} -eq $n ]] && break
540 if [[ "${#pac_deps[@]}" -ne 0 ]]; then
541 # check a list quickly for availability
542 while IFS
=\' read -r _ dep _
; do
543 # check only the error cases as a possible provides
544 pacman
-Ssq -- "^$dep$" &>/dev
/null || errorout
"$dep: dependency cannot be met."
545 done < <(pacman
-Si -- "${pac_deps[@]}" 2>&1 >/dev
/null
)
548 pac_deps
=( $
(sort -u <(printf '%s\n' "${pac_deps[@]}")) )
550 echo; warn
"the following (${#pac_deps[@]}) packages will be installed by pacman: ${pac_deps[@]}"; echo
556 # add_targets_to_list {{{
557 add_targets_to_list
() {
558 local name dep Name Version URLPath
560 # are we adding these to be installed --asdeps?
564 if grep -Fq -- " $name " <<< " $ignore_packages "; then
565 if ! prompt
"$name is in ignore_packages. Install anyway"; then
566 warn
"skipping target: $name"
572 read -r Name Version URLPath
< <(wget
-q -O - "${aur_info}$(url_encode "$name")" |
sed -e '/.*"Name":"\([^"]*\)".*"Version":"\([^"]*\)".*"URLPath":"\([^"]*\)".*/!d; s//\1 \2 \3/g')
573 [[ -z "$Name" ]] && errorout
"$name: package not found in AUR."
575 # is it already up to date?
576 grep -Fqx -- "$Name $Version" "$tmpdir/installed.lst" && warn
"$Name-$Version is up to date -- reinstalling"
578 # add non-dupes to list of targets
579 if ! grep -Fx -- " $Name " <<< " ${list_names[@]} "; then
580 list_names
+=("$Name")
581 list_versions
+=("$Version")
582 list_urls
+=("$URLPath")
592 [[ "${#args[@]}" -eq 0 ]] && errorout
'no targets specified (use -h for help)'
594 $resolve_dependencies && dep_check
596 list_names
=(); list_versions
=(); list_urls
=(); list_deps
=()
597 pacman
-Qm > "$tmpdir/installed.lst"
599 echo 'searching AUR...'
600 add_targets_to_list
1 "${aur_deps[@]}"
601 add_targets_to_list
0 "${args[@]}"
603 if [[ ${#list_names[@]} -ne 0 ]]; then
604 echo -en "\n${colorY}Targets (${#list_names[@]}):${nocolor} "
606 for ((i
=0; i
<${#list_names[@]}; i
++)); do
607 echo -n "${list_names[i]}-${list_versions[i]} "
610 prompt
"\n\nProceed with installation" || clean_exit
613 errorout
'no targets specified (use -h for help)'
619 # process_targets {{{
627 for ((i
=0; i
<${#list_names[@]}; i
++)); do
628 Name
="${list_names[i]}"
629 Version
="${list_versions[i]}"
630 URLPath
="${list_urls[i]}"
631 dep
="${list_deps[i]}"
634 pkg
="$(find_pkg "$Name" "$Version")"
636 if ! $rebuild && [[ -n "$pkg" ]] && [[ "$sync_mode" = 'install' ]] && ! is_devel
"$pkg"; then
638 display
"Installing cached $pkg..."
640 # download from aur, sets $pkg variable
641 download_tarball
"$Name" "$URLPath"
642 build_package
"$dep" ||
continue
645 install_package
"$pkg" "$dep"
648 # remove make depends
649 if $remove_makedepends && [[ ${#mk_deps[@]} -ne 0 ]]; then
650 display
'Removing make depends...'
651 for mk_dep
in ${mk_deps[@]}; do
652 pacman
--noconfirm -R $mk_dep || warn
"unable to remove makedepend $mk_dep"
661 # just true/false and non-config options
663 dont_discard_devels
=true
664 resolve_dependencies
=false
665 remove_makedepends
=false
672 # default to no color
687 local config _sourced
=false
689 # global config file is always sourced
690 config
='/etc/aurgetrc'
691 if [[ -r "$config" ]]; then
692 .
"$config" || errorout
"$config: invalid configuration"
696 # try xdg directory first
697 config
="${XDG_CONFIG_HOME:-$HOME/.config}/aurgetrc"
698 if [[ -r "$config" ]]; then
699 .
"$config" || errorout
"$config: invalid configuration"
703 config
="$HOME/.aurgetrc"
704 if [[ -r "$config" ]]; then
705 .
"$config" || errorout
"$config: invalid configuration"
711 warn
'no config found, this could get weird...'
719 local IFS
=':' conf confs _PKGDEST
721 # todo: preserve a PKGDEST environment variable?
723 # source these in order
724 confs
="/etc/makepkg.conf:$HOME/.makepkg.conf"
726 for conf
in $confs; do
727 [[ -r "$conf" ]] && .
"$conf"
730 # set package_directory
731 package_directory
="${package_directory:-$PKGDEST}"
732 package_directory
="${package_directory:-$PWD}"
733 [[ -d "$package_directory" ]] || mkdir
-p "$package_directory"
735 # set build_directory
736 # don't use SRCDEST, do check deprecated source_directory
737 #build_directory="${source_directory:-$SRCDEST}"
738 build_directory
="${build_directory:-$source_directory}"
739 build_directory
="${build_directory:-$PWD}"
740 [[ -d "$build_directory" ]] || mkdir
-p "$build_directory"
742 # this will always be defined by makepkg.conf
743 pkgext
="${PKGEXT:-.pkg.tar.gz}"
750 local _opmode
='' donocolor
=false
752 # global var, used by main
755 while [[ -n "$1" ]]; do
756 [[ -n "$opmode" ]] && _opmode
="$opmode"
759 -h|
--help) message
;;
760 -v|
--version) version_info
;;
762 -S) opmode
='install' ;;
763 -Sy) opmode
='install'; sync_mode
='install' ;;
764 -Sb) opmode
='install'; sync_mode
='build' ;;
765 -Sd) opmode
='install'; sync_mode
='download' ;;
766 -Su) opmode
='upgrade' ;;
767 -Syu|
-Suy) opmode
='upgrade'; sync_mode
='install' ;;
768 -Sbu|
-Sub) opmode
='upgrade'; sync_mode
='build' ;;
769 -Sdu|
-Sud) opmode
='upgrade'; sync_mode
='download' ;;
770 -Ss) opmode
='search' ; search_mode
='search' ;;
771 -Ssq|
-Sqs) opmode
='search' ; search_mode
='quiet' ;;
772 -Si|
-Ssi|
-Sis) opmode
='search' ; search_mode
='info' ;;
773 -Sp|
-Ssp|
-Sps) opmode
='search' ; search_mode
='print' ;;
774 --ignore) shift; ignore_packages
+=" $1" ;;
775 --mopt) shift; mopts
+=" $1" ;;
776 --popt) shift; popts
+=" $1" ;;
777 --nocolor) donocolor
=true
;;
778 --noconfirm) noconfirm
=true
;;
779 --rebuild) rebuild
=true
;;
780 --asroot) asroot
=true
;;
781 --devel) devels
=true
;;
782 --deps) resolve_dependencies
=true
;;
783 --nodeps) resolve_dependencies
=false
;;
784 --edit) edit_pkgbuilds
='always' ;;
785 --noedit) edit_pkgbuilds
='never' ;;
786 --discard) discard_sources
=true
;;
787 --nodiscard) discard_sources
=false
;;
788 --*=*) runtime_flag
"$1" ;;
794 # show help if multiple -S* commands are passed
795 [[ -n "$_opmode" ]] && [[ "$_opmode" != "$opmode" ]] && message
798 # grab possible args after --
799 [[ $
* ]] && args
+=("$@")
802 [[ "$edit_pkgbuilds" =~ ^
(always|never|prompt
)$
]] || edit_pkgbuilds
='prompt'
803 [[ "$sync_mode" =~ ^
(download|build|
install)$
]] || errorout
'invalid sync mode specified'
804 [[ "$search_mode" =~ ^
(search|info|quiet|print
)$
]] || errorout
'invalid search mode specified'
806 # no dep checking unless installing
807 [[ "$sync_mode" != 'install' ]] && resolve_dependencies
=false
809 # (re)set user binaries
810 pacman
="${pacman_command:-sudo pacman -U}"
811 makepkg
="${makepkg_command:-makepkg --noconfirm}"
812 editor
="${pkgbuild_editor:-$EDITOR}"
813 editor
="${editor:-vi}" # a failsafe
815 # (re)set dev regex, it can't be blank
816 development_regex
=${development_regex:-.*-git$\|.*-hg$\|.*-svn$\|.*-darcs$\|.*-cvs$\|.*-bzr$}
818 # prevent root unless desired
819 if [[ $
(id
-u) -eq 0 ]]; then
820 $asroot || errorout
'You must pass --asroot to allow running aurget as root (not recommended).'
823 pacman
="${pacman/sudo /}"
828 # change some things to go along with --noconfirm
829 grep -Fqw -- '--noconfirm' <<< "$popts" || popts
+=' --noconfirm'
830 grep -Fqw -- '--noconfirm' <<< "$mopts" || mopts
+=' --noconfirm'
831 edit_pkgbuilds
='never'
834 # if resolving deps, tell makepkg so
835 if $resolve_dependencies; then
836 grep -Fqw -- '--syncdeps' <<< "$mopts" || mopts
+=' --syncdeps'
840 if $donocolor ||
[[ ! -t 1 ]]; then
841 # disable pacman color
842 pacman
="${pacman/pacman-color/pacman}"
844 # disable makepkg color
845 grep -Fqw -- '--nocolor' <<< "$mopts" || mopts
+=' --nocolor'
847 # disable aurget color
865 install) setup_targets
;;
866 search
) search_aur
;;
875 [[ -d "$tmpdir" ]] && rm -r "$tmpdir"
884 [[ "$1" != 'bootstrap' ]] && return
888 Boot-strapping aurget...
890 Once built, please use pacman to install the package in this
901 build_directory
="$tmpdir/bootstrap"
908 # clean up if we catch signals
909 trap 'clean_exit 1' TERM HUP QUIT INT
911 aur
='https://aur.archlinux.org'
912 aur_srch
="$aur/rpc.php?type=search&arg="
913 aur_info
="$aur/rpc.php?type=info&arg="
914 aur_pkgs
="$aur/packages"
917 lock_file
="$tmpdir/aurget.lock"
924 flock
-n 9 || dieonlock
934 # vim:foldmethod=marker foldmarker={{{,}}}