#!/usr/bin/bash # 2015-07-18 git-aurcheck by severach (GPLv3+) # -TODO: Version checker # -TODO: ability to specify and exclude packages on the command line # -TODO: Ability to elevate the version check return code # -TODO: Switch origin on git packages from aur4 to aur (if necessary) # -TODO: (TBD) pkgver=[a-z] not allowed in more pedantic mode # -TODO: single watch variable # TODO: Better link scanner for version checker, perl or xidel maybe? # TODO: An explain switch that outputs implementation details about less obvious warnings. # TODO: Github? # TODO: Average release cycle (daily, weekly, monthly, yearly) # TODO: (TBD) Convert pedantic level to warn/err values # TODO: Rearrange code to allow many checks without .git folder # TODO: man page # BUG: -v can take a long time. ^C leaves a temporary folder. # NOWAY: Version check in alternate file or against read-only remote links. # Won't be implemented. To minimize server abuse, version check is only # available to the PKGBUILD maintainer. Users must use 'cower -u' or another # AUR helper. If the maintainer won't check versions or implement _verwatch # then they need to be pressed into orphaning the package to someone who # will. # You can always cheat and clone with ssh. # a helper like namcap that checks and offers solutions for common problems in git submissions to the AUR # git is hard to learn, has a command sequence a bit over the top for AUR # submissions, has dangerous command line defaults, and the AUR git server # checks for little more than a missing .SRCINFO while reporting a horde of # nonsensical errors. This checks a planned git submission and suggests # appropriate next steps along with complete command lines with the safest # available options. # Commands that are safe and desirable are offered complete and ready to paste # with the safe command line options. # Commands that may not be safe or desirable are suggested but no pastable # form is offered. # Rather than guess the next steps and be wrong more often than not, run this # over and over. All possible next steps are shown but only a few may be # appropriate. Perform the first related steps and run again for updated next # steps. # Option -h for help. # Return: # 0 = all good # 1 = some packages had warnings # 2 = some packages had errors # 3 = some packages had severe errors # nn = some packages have version updates (choose nn with -V) # 99 = some packages haven't been upgraded to AUR git # >=126 = something went wrong (probably your fault) # See below for how to change the origin if you are seeing packages # you don't have write access to. # List of AUR helpers that also check versions: # pkglivecheck (defunct), pkgbuild-watch, pkgcheck, pkgoutofdate # So far as I can tell, none of these used a sed expression to extract perfect # version numbers or used vercmp to properly compare them. # Checked with ShellCheck set -u set -e # This is tricky to code around. if [ ! -s ~/.git-aurcheck ]; then cat > ~/.git-aurcheck << EOF # Name configuration file for git-aurcheck, Arch Linux AUR4 # Enter a grep compatible search to allow git-aurcheck to detect your maintainer line _opt_Maintainer='none' #_opt_Maintainer='^# Maintainer:\s\+Your Name Here' EOF fi source ~/.git-aurcheck if [ "${_opt_Maintainer:=none}" = 'none' ]; then echo "Warning: Add your name to ~/.git-aurcheck for proper maintainer detection" echo "" sleep 1 fi _opt_VERSION='0.62' _opt_AUR4='aur' # After August 8, these 3 time bomb lines can be removed and _opt_AUR4 can be gotten rid of altogether #if [ "$(date +'%s')" -lt 1439006400 ]; then # date +%s -d'2015-08-08' # _opt_AUR4='aur4' #fi for _var_cmd in 'mksrcinfo' 'md5sum' 'pcre2grep'; do if ! command -v "${_var_cmd}" >/dev/null 2>&1; then echo -e "Error: Required program could not be found: ${_var_cmd}" echo -e 'Please install pkgbuild-introspection or other supporting package' exit 126 fi done unset _var_cmd if [ "${EUID}" -eq 0 ]; then echo "root doesn't make packages. Please run as a user." exit 127 fi # This dummy function supresses errors in package aur ksh srcinfo_write_attr() { : } export -f srcinfo_write_attr # A special starts-with version of find that hunts for foo*, usually used with 'foo=' _var_findst_in=' _fn_findst_in_@@@() { if [ "${#_var_@@@[@]}" -ne 0 ]; then local _var_find for _var_find in "${_var_@@@[@]}"; do case "${_var_find}" in "$1"*) return 0;; esac done fi return 1 } ' # A special starts-with version of find that hunts for foo or foo=version _var_findver_in=' _fn_findver_in_@@@() { if [ "${#_var_@@@[@]}" -ne 0 ]; then local _var_find for _var_find in "${_var_@@@[@]}"; do case "${_var_find}" in "$1"|"$1="*) return 0;; esac done fi return 1 } ' _var_find_in=' _fn_find_in_@@@() { if [ "${#_var_@@@[@]}" -ne 0 ]; then local _var_find for _var_find in "${_var_@@@[@]}"; do if [ "${_var_find}" = "$1" ]; then return 0 fi done fi return 1 } ' _opt_find_in="${_var_find_in//_var_@@@/_opt_@@@}" eval "${_opt_find_in//@@@/EXCLUDES}" eval "${_var_findst_in//@@@/provides}" eval "${_var_findver_in//@@@/provides}" eval "${_var_find_in//@@@/conflicts}" eval "${_var_find_in//@@@/depends}" eval "${_var_find_in//@@@/makedepends}" eval "${_var_find_in//@@@/wantmakedepends}" eval "${_var_find_in//@@@/gitadds}" eval "${_var_find_in//@@@/allfiles}" eval "${_var_find_in//@@@/srcfileswantgit}" eval "${_var_find_in//@@@/srcfilesdontwantgit}" eval "${_var_find_in//@@@/srcfiles}" eval "${_var_find_in//@@@/gitfiles}" unset _opt_find_in _var_find_in _var_findver_in _var_findst_in # This accepts no parameters. The caller must cd to the directory to be checked. _fn_aurcheck() { local _var_pwd _var_pwd="$(pwd)" local _var_pwdbn _var_pwdbn="$(basename "${_var_pwd}")" if [ -d '.git' ]; then # This is \n safe only because git escapes non printing characters # https://stackoverflow.com/questions/28417414/can-i-put-strings-divided-by-newlines-into-an-array-using-the-read-builtin local _var_gitfiles=() # because git-status --porcelain doesn't show all files # Despite claims, this still produces a non zero return code IFS=$'\n' read -r -d '' -a _var_gitfiles < <(git ls-files; echo -n $'\0') || : if [ "${#_var_gitfiles[@]}" -ne 0 ]; then local _var_idx local _var_file for _var_idx in "${!_var_gitfiles[@]}"; do _var_file="${_var_gitfiles[${_var_idx}]}" if [ "${_var_file: 0:1}" = '"' ] && [ "${_var_file: -1:1}" = '"' ]; then _var_gitfiles[${_var_idx}]="$(printf '%b' "${_var_file: 1:-1}")" # unescape file name fi done unset _var_idx unset _var_file fi local _var_allfiles=() if [ "${#_var_gitfiles[@]}" -ne 0 ]; then _var_allfiles=("${_var_gitfiles[@]}") fi if ! :; then # This code was part of revision 2 which may be needed again if we decide to decode git-status local _var_gitlines=() IFS=$'\n' read -r -d '' -a _var_gitlines < <(git status --porcelain; echo -n $'\0') || : local _var_gitline local _var_statIDX='' # Index local _var_statWT='' # Worktree local _var_statQM='' # ? if either are ? for _var_gitline in "${_var_gitlines[@]}"; do local _var_gitlinef="${_var_gitline:3}" # git status FILE -> FILE is not handled. Paths are not handled. if [ "${_var_gitlinef: 0:1}" = '"' ] && [ "${_var_gitlinef: -1:1}" = '"' ]; then _var_gitlinef="$(printf '%b' "${_var_gitlinef: 1:-1}")" # unescape file name fi if [ "${_var_gitlinef}" = "${_var_gitfile}" ]; then _var_statIDX="${_var_gitline: 0:1}" _var_statWT="${_var_gitline: 1:1}" [ "${_var_statIDX}" = '?' ] || [ "${_var_statWT}" = '?' ] && _var_statQM='?' break fi done fi local _var_gitadds=() declare -A _var_644 # this is also the required files array. _var_644['PKGBUILD']=' ' # neither ${:-default} nor ${:+exists} can tell the difference between '' blank and unset so we must make these non blank. I don't see any other way short of trap. _var_644['.SRCINFO']=' Try: mksrcinfo' # pcregrep and pcre2grep don't have an autoexpanding buffer so they are not suitable for binary files # Error: line 3400 of file ... is too long for the internal buffer; check the --buffer-size option # We have no way to predict how large that buffer will need to be so we must disable binary searches: pcregrep -I local _var_forbidgreps=('/sbin|! grep -l% "/sbin"' '/usr/tmp|! grep -l% "/usr/tmp"' '/usr/local|! grep -l% "/usr/local"' '/bin|! pcre2grep -Il% "(? "${_PKGBUILDtmp}" cat 'PKGBUILD' >> "${_PKGBUILDtmp}" echo '}' >> "${_PKGBUILDtmp}" source "${_PKGBUILDtmp}" rm -f "${_PKGBUILDtmp}" unset _PKGBUILDtmp local _var_line local _var_infunc='' local _var_thisftext='' local _var_thisfunc='' while IFS='' read -r _var_line; do #printf '%b\n' "${_var_line}" if [ "${#_var_line}" -ge 4 ] && [ "${_var_line: 0:4}" = ' ' ]; then local _var_cleanline="${_var_line% }" _var_cleanline="${_var_cleanline%;}" _var_PBall="${_var_PBall}${_var_cleanline# }"$'\n' unset _var_cleanline fi case "${_var_line}" in ' function '*' () ') _var_infunc="${_var_line# function }" _var_infunc="${_var_infunc% () }" _var_thisftext='' _var_thisfunc="${_var_line# }"$'\n' ;; ' };'|' }') _var_thisfunc="${_var_thisfunc}${_var_line# }"$'\n' # Some end braces are not functions if [ ! -z "${_var_infunc}" ] && [ ! -z "${_var_PBfuncAll[${_var_infunc}]:-}" ]; then echo "Warning: function ${_var_infunc}() defined multiple times" [ "${returnv}" -ge 1 ] || returnv=1 fi if [ ! -z "${_var_infunc}" ]; then _var_PBfuncAll[${_var_infunc}]="${_var_thisfunc}" _var_PBftextAll[${_var_infunc}]="${_var_thisftext}" case "${_var_infunc}" in 'pkgver'|'prepare'|'build'|'package'*) _var_PBftextArch[${_var_infunc}]="${_var_thisftext}";; esac fi _var_infunc='' _var_thisftext='' _var_thisfunc='' ;; ' { ');; ' '*) if [ -z "${_var_infunc}" ]; then _var_line="${_var_line% }" _var_line="${_var_line%;}" _var_PBtop="${_var_PBtop}${_var_line# }"$'\n' else _var_thisfunc="${_var_thisfunc}${_var_line# }"$'\n' _var_thisftext="${_var_thisftext}${_var_line# }"$'\n' fi ;; esac done < <(declare -f _fn_git-aurcheck) unset _var_line unset _var_infunc unset _var_thisfunc unset _var_thisftext #set > x # # the function list is needed in so many places. Lets get a complete list. # _fn_runpkgbuild() { # set -u # source 'PKGBUILD' # set -u # declare -A _var_funclist # local _var_func # for _var_func in $(declare -fF | cut -d' ' -f3 | grep '^pkgver$\|^prepare$\|^build$\|^package'); do # notice package lacks the $ # builtin printf "_var_funclist[${_var_func}]=%q\n" "$(declare -f ${_var_func})" # done # } # export -f _fn_runpkgbuild # eval "$(bash -c _fn_runpkgbuild)" # unset -f _fn_runpkgbuild # local _var_func # for _var_func in "${!_var_funclist[@]}"; do # case "${_var_func}" in # pkgver|prepare|build|package*) _var_funclistPB[${_var_func}]="${_var_funclist[${_var_func}]}";; # esac # done # unset _var_func # We can expand all these strings and arrays any way we want and look at everything in great detail. Let's make bash sing! if [ "${_opt_PEDANTIC}" -ge 1 ]; then local _var_orders=('pkgbase' 'pkgname' 'pkgver' 'pkgrel' 'epoch' 'pkgdesc' 'arch' 'url' 'license' 'groups' 'depends*' 'optdepends*' 'makedepends*' 'checkdepends*' 'provides*' 'conflicts*' 'replaces*' 'backup' 'options' 'install' 'changelog' 'source*' 'noextract' 'validpgpkeys' 'md5sums*' 'sha1sums*' 'sha224sums*' 'sha256sums*' 'sha384sums*' 'sha512sums*') # We make no attempt to order indented PKGBUILD variables or var+= readarray -t _var_topvars < <(sed -n -e 's:^\([a-z][a-z0-9A-Z_]\+\)=.*$:\1:p' <<<"${_var_PBtop}") # A special find tool that searches $1 in a list like (foo foo*) # $1 = variable to look for like xyzzy, pkgver, or source_i686 # the orders list would contain ('pkgver' 'source*') respectively # xyzzy is invalid and returns -1 # orders pkgver isn't followed by a * so must match $1 exactly # orders source is followed by a * so can match $1=source or $1=source_foobar. The arch list is ever expanding making it impractical to restrict the search to known arches. # returns the array number in orders 0..n-1 or '-1' if not found _fn_findst_in_orders() { if [ "${#_var_orders[@]}" -ne 0 ]; then local _var_findno local _var_find for _var_findno in "${!_var_orders[@]}"; do _var_find="${_var_orders[${_var_findno}]}" case "${_var_find}" in *'*') case "$1" in "${_var_find%\*}"|"${_var_find%\*}_"*) echo "${_var_findno}"; return 0;; esac ;; *) if [ "$1" = "${_var_find}" ]; then echo "${_var_findno}" return 0 fi ;; esac done fi echo '-1' return 0 } # A special find tool that searches $1 for it's position <= in a supposed-to-be ascending list. Any number in the list not ascending is ignored and skipped. # $1 = number to look for like xyzzy, pkgver, or source_i686 # $2 = 'b' for before, 'a' after # returns the index value 0..n-1. Generally this index is used on a cooresponding array. # before: If we hunt for 5 in the list (1 -1 3 92 6 8 27), -1 and 92 are skipped so the <= test lands on 6. The return value is the array index 4 because 5 should be placed before 6 _fn_findst_in_topords() { local _var_findnop=0 if [ "${#_var_topords[@]}" -ne 0 ]; then local _var_findno local _var_find local _var_ord=0 for _var_findno in "${!_var_topords[@]}"; do _var_find="${_var_topords[${_var_findno}]}" #echo "${_var_findno} ord: ${_var_find} -ge ${_var_ord}, findnop=${_var_findnop}" 1>&2 if [ "${_var_find}" -ge "${_var_ord}" ]; then _var_ord="${_var_find}" [ "$2" = 'b' ] && _var_findnop="${_var_findno}" #echo "${_var_findno} cmp: ${_var_ord} -ge $1, findnop=${_var_findnop}" 1>&2 if [ "${_var_ord}" -ge "$1" ]; then break fi [ "$2" = 'a' ] && _var_findnop="${_var_findno}" fi done fi echo "${_var_findnop}" return 0 } # Note that we do not attempt to order the arch arrays so (foo_x86_64 foo foo_i686) generates no warning. People can apply their own preferences. # With this outrageous check I think we've moved from pedantic to creepy! local _var_topords=() local _var_topvar local _var_ord=-1 local _var_nextord for _var_topvar in "${_var_topvars[@]}"; do _var_nextord="$(_fn_findst_in_orders "${_var_topvar}")" _var_topords+=("${_var_nextord}") #echo "${_var_topvar}: ${_var_nextord} -lt ${_var_ord}" if [ "${_var_nextord}" -eq -1 ]; then echo "Warning: Custom top variable ${_var_topvar} should be prefixed with a _ to avoid conflict with makepkg variables" [ "${returnv}" -ge 1 ] || returnv=1 elif [ "${_var_nextord}" -lt "${_var_ord}" ]; then echo "Warning: Top variable ${_var_topvar} goes before ${_var_topvars[$(_fn_findst_in_topords "${_var_nextord}" 'b')]}" # " [ "${returnv}" -ge 1 ] || returnv=1 else _var_ord="${_var_nextord}" fi done unset _var_topvar unset _var_orders unset _var_ord unset _var_nextord unset -f _fn_findst_in_orders unset -f _fn_findst_in_topords unset _var_topvars unset _var_topords fi #set > x #local _var_funcname # Interesting that ${!foo[@]} can work on empty arrays but "${foo[@]}" cannot #for _var_funcname in "${!_var_PBftextAll[@]}"; do # This isn't possible without extensive code analysis. package has globals. Other functions can local foo; foo=x #if grep -ql '^\s*[a-zA-Z0-9_]\+=' <<<"${_var_PBftextAll[${_var_funcname}]}"; then # echo "Warning: function ${_var_funcname} contains non local variables" # [ "${returnv}" -ge 1 ] || returnv=1 #fi #done #unset _var_funcname # I could detect the lack of a package function but why bother? makepkg already does this split or not split. if grep -ql '||\s*return\s\+1\s*$' <<<"${_var_PBall}"; then echo "Warning: '|| return 1' deprecated. Please remove. makepkg does this with 'set -e'" [ "${returnv}" -ge 1 ] || returnv=1 fi if grep -ql '\${*startdir' <<<"${_var_PBall}"; then echo 'Warning: ${startdir} deprecated and should be removed. It can often be changed to ${srcdir}.' [ "${returnv}" -ge 1 ] || returnv=1 fi local _var_funcname for _var_funcname in "${!_var_PBftextArch[@]}"; do if [ "${_opt_PEDANTIC}" -ge 1 ]; then if ! grep -qlF 'set -u' <<<"${_var_PBftextArch[${_var_funcname}]}" || ! grep -qlF 'set +u' <<<"${_var_PBftextAll[${_var_funcname}]}"; then echo "Warning: function ${_var_funcname} should be surrounded by set -u and set +u" [ "${returnv}" -ge 1 ] || returnv=1 fi fi if [ "${_var_funcname}" = 'prepare' ] && grep -ql '^\s*make' <<<"${_var_PBftextArch[${_var_funcname}]}"; then echo "Warning: make should be moved from ${_var_funcname}() into build() or possibly package()." [ "${returnv}" -ge 1 ] || returnv=1 fi if [ "${_var_funcname}" != 'prepare' ] && grep -ql '^\s*tar\|^\s*bsdtar\|^\s*unzip' <<<"${_var_PBftextArch[${_var_funcname}]}"; then echo "Warning: tar,bsdtar,unzip should be moved from ${_var_funcname}() into prepare()." [ "${returnv}" -ge 1 ] || returnv=1 fi if [ "${_var_funcname}" != 'prepare' ] && grep -ql '^\s*cmake' <<<"${_var_PBftextArch[${_var_funcname}]}"; then echo "Warning: cmake should be moved from ${_var_funcname}() into prepare()." [ "${returnv}" -ge 1 ] || returnv=1 fi if [ "${_var_funcname}" != 'prepare' ] && grep -qlF './configure' <<<"${_var_PBftextArch[${_var_funcname}]}"; then echo "Warning: configure should be moved from ${_var_funcname}() into prepare()." [ "${returnv}" -ge 1 ] || returnv=1 fi # We could catch nawk too but that's pretty rare. if [ "${_var_funcname}" = 'build' ] && grep -ql '^\s*sed\|^\s*awk\|^\s*patch\|^\s*gawk' <<<"${_var_PBftextArch[${_var_funcname}]}"; then echo "Warning: sed,awk,patches should be moved from ${_var_funcname}() into prepare() or possibly package()." [ "${returnv}" -ge 1 ] || returnv=1 fi done unset _var_funcname # Seems that set -x doesn't do this. There's some command like exit, return, set -e, or set -x that causes updpkgsums to zero length the PKGBUILD. I'll need to have it happen again to see which command causes it. #if grep -ql '^\s*set [+-]x' <<<"${_var_PBtop}"; then # echo '******************************************************' # echo 'Your PKGBUILD top level contains a set -x or set +x which will cause updpkgsums to destroy the PKGBUILD' # echo 'THESE COMMANDS MUST BE REMOVED IMMEDIATELY!' # echo '******************************************************' # [ "${returnv}" -ge 2 ] || returnv=2 #fi if grep -ql '^\s*sudo' <<<"${_var_PBall}"; then echo 'sudo is not an allowed command in a PKGBUILD!' [ "${returnv}" -ge 3 ] || returnv=3 fi # rm -f "${_PKGBUILDtmp}" # unset _PKGBUILDtmp if [ "${_opt_PEDANTIC}" -ge 1 ]; then if ! grep -ql '^set -u' <<<"${_var_PBtop}" || ! grep -ql '^set +u' <<<"${_var_PBtop}"; then echo 'Warning: surrounding the PKGBUILD with set -u, set +u will help catch script errors.' [ "${returnv}" -ge 1 ] || returnv=1 fi if [ "${_opt_PEDANTIC}" -ge 3 ]; then # These test commands are specially crafted to fail correctly with set -e local _var_forbiddens=('/bin|! test -d "${pkgdir}/bin"' '/sbin|! test -d "${pkgdir}/sbin"' '/lib|! test -d "${pkgdir}/lib"' '/share|! test -d "${pkgdir}/share"' '/usr/sbin|! test -d "${pkgdir}/usr/sbin"' '/usr/local|! test -d "${pkgdir}/usr/local"') local _var_forbidden for _var_forbidden in "${_var_forbidgreps[@]}"; do _var_forbiddens+=("${_var_forbidden//%/r}"' "${pkgdir}"') # [@] probably won't work done local _var_forbiddenspr=() for _var_forbidden in "${_var_forbiddens[@]}"; do _var_forbiddenspr+=("${_var_forbidden#*|}" "${_var_forbidden%%|*}") done for _var_forbidden in "${_var_forbiddens[@]}"; do # These can be commented out so we must search the PKGBUILD directly if ! grep -qlF "${_var_forbidden#*|}" 'PKGBUILD'; then echo ' # Ensure there are no forbidden paths. Place at the end of package() and comment out as you find or need exceptions. (git-aurcheck)' printf ' %s || { echo "Line ${LINENO} Forbidden: %s"; false; }\n' "${_var_forbiddenspr[@]}" # Not sure why the extra ; is required [ "${returnv}" -ge 1 ] || returnv=1 break fi done unset _var_forbiddenspr unset _var_forbiddens unset _var_forbidden fi if ! grep -ql '^\s*sha256sums=' <<<"${_var_PBtop}" && ! grep -ql '^\s*sha384sums=' <<<"${_var_PBtop}" && ! grep -ql '^\s*sha512sums=' <<<"${_var_PBtop}"; then echo 'Warning: sha256sums or better are recommended. Check existing sums before upgrading.' [ "${returnv}" -ge 1 ] || returnv=1 fi if grep -ql '^\s*md5sums=' <<<"${_var_PBtop}" || grep -ql '^\s*sha1sums=' <<<"${_var_PBtop}" || grep -ql '^\s*sha224sums=' <<<"${_var_PBtop}"; then echo 'Warning: lesser md5sums,sha1sums,sha224sums should be removed.' [ "${returnv}" -ge 1 ] || returnv=1 fi # Let's get them in the comments so search the entire PKGBUILD if grep -qlE '\$(pkgbase|pkgname|pkgver|srcdir|pkgdir|startdir|CARCH)' 'PKGBUILD'; then echo 'Warning: $vars should be converted to ${vars}.' [ "${returnv}" -ge 1 ] || returnv=1 fi # Perfectly legit and we can't detect it: if ! :; then false; fi # All I can do is color them red in mc PKGBUILD.syntax # Here the best I can do is detect them intentionally non indented which is where I put them to be noticable. if grep -ql '^false' 'PKGBUILD'; then echo 'Warning: Your PKGBUILD contains a lone false and is unlikely to work' [ "${returnv}" -ge 1 ] || returnv=1 fi fi # I'd like to recommend more $var to ${var} changes but I don't see a way to do it in grep. if [ "${_opt_Maintainer}" != 'none' ] && ! grep -ql "${_opt_Maintainer}" 'PKGBUILD'; then echo 'Warning: You are not listed as a maintainer in PKGBUILD.' [ "${returnv}" -ge 1 ] || returnv=1 fi local _var_tempdir _var_tempdir="$(basename "$0")" # mksrcinfo doesn't let us specify a target file _var_tempdir="$(mktemp -p '/tmp' -d "${_var_tempdir}.XXXXX")" # ^C always breaks at curl version check which leaves this folder. Making the folder in /tmp allows to get out of cleaning it up. It also eliminates changes to the build folders timestamps during update checks. if pushd "${_var_tempdir}" >/dev/null; then ln -s "${_var_pwd}/PKGBUILD" if mksrcinfo; then # Use the updated .SRCINFO if we can get it # Every reader here must be copied below _var_install="$(sed -ne 's:^\tinstall = \(.\+\)$:\1:p' '.SRCINFO')" _var_changelog="$(sed -ne 's:^\tchangelog = \(.\+\)$:\1:p' '.SRCINFO')" _var_pkgbase="$(sed -ne 's:^pkgbase = \(.\+\)$:\1:p' '.SRCINFO')" IFS=$'\n' read -r -d '' -a _var_srcfiles < <(sed -ne 's:^\tsource = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : IFS=$'\n' read -r -d '' -a _var_pkgnames < <(sed -ne 's:^pkgname = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : IFS=$'\n' read -r -d '' -a _var_makedepends < <(sed -ne 's:^\tmakedepends = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : IFS=$'\n' read -r -d '' -a _var_depends < <(sed -ne 's:^\tdepends = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : IFS=$'\n' read -r -d '' -a _var_conflicts < <(sed -ne 's:^\tconflicts = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : IFS=$'\n' read -r -d '' -a _var_provides < <(sed -ne 's:^\tprovides = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : IFS=$'\n' read -r -d '' -a _var_pkgvernew < <(sed -ne 's:^\tpkgver = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : IFS=$'\n' read -r -d '' -a _var_pkgrelnew < <(sed -ne 's:^\tpkgrel = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : if [ ! -s "${_var_pwd}/.SRCINFO" ] || [ "$(grep -v '^#' '.SRCINFO' | md5sum)" != "$(grep -v '^#' "${_var_pwd}/.SRCINFO" | md5sum)" ]; then echo ".SRCINFO is missing or out of date. Try 'mksrcinfo'" [ "${returnv}" -ge 2 ] || returnv=2 _var_gitadds+=('.SRCINFO') # this is the first add so no dups possible fi # https://bbs.archlinux.org/viewtopic.php?id=202062 Don't use $pkgname in paths in split packages: example of libsystemd if [ "${#_var_pkgnames[@]}" -ge 2 ] && grep -qlF $'${pkgname}\n$pkgname' <<<"${_var_PBftextAll[@]:-}"; then # # Extract listing of PKGBUILD defined functions compiled and comments removed by bash. This doesn't get functions defined by eval. # # We want only the functions. $pkgname is approprate in the top non function area # _fn_runpkgbuild() { # set -u # source "${_var_pwd}/PKGBUILD" # set -u # _foo=$(declare -fF | cut -d' ' -f3 | grep '^pkgver$\|^prepare$\|^build$\|^package') # notice package lacks the $ # declare -f ${_foo} # } # export -f _fn_runpkgbuild # local _var_findinfunc # _var_findinfunc="$(_var_pwd="${_var_pwd}" bash -c _fn_runpkgbuild)" # unset -f _fn_runpkgbuild #if grep -qlF $'${pkgname}\n$pkgname' <<<"${_var_PBftextall[@]}"; then # We can recommend $pkgbase for non split packges but that's too pedantic, even for me! echo 'Warning: Using ${pkgname} in any function of a split package is unsafe. Use ${pkgbase}.' [ "${returnv}" -ge 1 ] || returnv=1 #fi #unset _var_findinfunc fi # We could easily convert this entirely into the new arrays. if [ "${_opt_VERSION}" -ne 0 ] && { grep -qlF $'_verwatch=' <<<"${_var_PBtop}" || test ! -z "${_var_PBftextAll[_vercheck]:-}"; }; then _fn_runpkgbuild() { set -u source "${_var_pwd}/PKGBUILD" set +u if declare -fF _vercheck >/dev/null; then : elif [ "${#_verwatch[@]}" -eq 3 ]; then : elif [ "${#_verwatch[@]}" -eq 0 ]; then #echo 'To use version checking either _verwatch or _vercheck() must exist' #echo 'For help, add to PKGBUILD: _verwatch=x' #return 1 return 0 # some packages contain _verwatch but don't ever execute it to make the variable come into existence. This is not an error. else echo 'To use version checking place a 3 element array _verwatch in PKGBUILD' >&2 echo '#1: a link for curl to download that contains the text of the updates' >&2 echo "#2: a string for sed -n -e 's:^...$:\1:p' that filters out the exact version text" >&2 echo ' the search string must include exactly one \(\) pair to satisfy \1' >&2 echo '#3: what to extract from the page: l=links, t=link text, f=all text' >&2 echo 'Example from mdadm-git:' >&2 echo "_verwatch=('ftp://ftp.kernel.org/pub/linux/utils/raid/mdadm/' 'mdadm-\(.*\)\.tar\.xz' 'f')" >&2 echo 'Example from adminer-editor:' >&2 echo "url='http://www.adminer.org/en/editor/'" >&2 echo '_verwatch=("${url}"'" 'http://downloads.sourceforge.net/adminer/editor-\([0-9\.]\+\).php' 'l')" >&2 return 1 fi set -u # http://stackoverflow.com/questions/1881237/easiest-way-to-extract-the-urls-from-an-html-page-using-sed-or-awk-only # A real getlinks would use an html decoder and not Cthulhu's sed+grep. # $1: l get link href (default), t get link text, f FTP listing or other no html declare -f -F _getlinks >/dev/null || _getlinks() { # We don't handle links split across line. We can easily improve on the Cthulhu madness: sed -e 's:\s\+$::g' -e 's:^\s\+::g' | tr '\n' ' ' | sed -e 's:<[^/]:\n&:g' | sed -e 's:\s\+$::g' | case "${1}" in l) grep -aF 'href=' | grep -ao '<[aA] .*href=.*>' | sed -e 's/<[aA] /\n]*\).*$/\1/p' -e '/^$/d';; t) grep -aF 'href=' | grep -ao '<[aA] .*href=.*>' | sed -e 's/<[aA] /\n]*>\([^<]*\)<.*$/\1/p' -e '/^$/d';; f) cat;; esac } # The PKGBUILD can replace any of these functions deemed necessary. The most likely to replace is _vercheck # Return sorted list of all version numbers available local _var_has_vercheck=0 if declare -f -F _vercheck >/dev/null; then _var_has_vercheck=1 else _vercheck() { local _versed2="${_verwatch[1]//:/\\:}" # Escape the two things that the PKGBUILD is not permitted to do _versed2="${_versed2//$/\\$}" # End of line (though sed doesn't seem to require this), and end of search curl -s -l "${_verwatch[0]}" | _getlinks "${_verwatch[2]}" | sed -ne "s:^${_versed2}"'$:\1:p' | sort -V } fi # Polling is better than version announcements. Everyone's poll cron time will be different. An announcement would generate an immediate traffic rush. # _vercheck and _verscan depend only on pacman, coreutils, sed, and grep # $1, return code 0=found update 1=no update found; $1=1 always return 0=true # $2, echo nothing, $2=1 echo latest file found, $2=2 echo all newer files found, $2=3 echo comparison and all files, $2=4 combo 1,3 declare -f -F _verscan >/dev/null || _verscan() { #local _pkgfile="${pkgname}-${pkgver}.tar.xz" local _rv=1 [ "$1" -ne 0 ] && _rv=0 local _verfound=0 local _rvfile='' local _remfile local IFS=$'\n' while read -r _remfile; do _verfound=1 local _vercmp _vercmp="$(vercmp "${_remfile}" "${pkgver}")" [ "$2" -ge 3 ] && printf '%-s %s\n' "${_vercmp}" "${_remfile}" 1>&2 if [ "${_vercmp}" -ge 1 ]; then [ "$2" -eq 2 ] && echo "${_remfile}" _rvfile="${_remfile}" _rv=0 fi done < <(_vercheck) #_vercheck 1>&2 if [ "${_verfound}" -eq 0 ]; then echo 'No version results' 1>&2 fi [ "$2" -eq 1 -o "$2" -eq 4 ] && echo "${_rvfile}" return ${_rv} } # If _vercheck is provided then the PKGBUILD is expected to do it's own thing. If _getlinks is used then the watch must still be provided. if [ "${_var_has_vercheck}" -ne 0 ] || [ ! -z "${_verwatch:-}" ]; then set -u if [ "${_opt_DEBUGVER}" -ne 0 ]; then _verscan 0 4 else _verscan 0 1 fi set -u fi } #set -u; _vercheck 0 3 && echo 'Update detected'; exit 1 #export _opt_DEBUGVER export -f _fn_runpkgbuild local _var_newver set +e _var_newver="$(_opt_DEBUGVER=${_opt_DEBUGVER} _var_pwd="${_var_pwd}" bash -c _fn_runpkgbuild)" set -e #unset _opt_DEBUGVER unset -f _fn_runpkgbuild if [ ! -z "${_var_newver}" ]; then echo "Warning: a new version ${_var_newver} may be available." [ "${returnv}" -ge "${_opt_VEREXITCODE}" ] || returnv="${_opt_VEREXITCODE}" fi unset _var_newver fi else echo 'Your PKGBUILD crashes with mksrcinfo.' [ "${returnv}" -ge 3 ] || returnv=3 fi cd "${_var_pwd}" rm -rf "${_var_tempdir}" popd >/dev/null # why don't pushd and popd have a --quiet option? else rmdir -f "${_var_tempdir}" fi unset _var_tempdir fi # -s 'PKGBUILD' # Nail the screwups no matter where they are! if [ -s '.SRCINFO' ]; then if [ -z "${_var_install}" ]; then _var_install="$(sed -ne 's:^\tinstall = \(.\+\)$:\1:p' '.SRCINFO')" fi if [ -z "${_var_changelog}" ]; then _var_changelog="$(sed -ne 's:^\tchangelog = \(.\+\)$:\1:p' '.SRCINFO')" fi if [ -z "${_var_pkgbase}" ]; then _var_pkgbase="$(sed -ne 's:^pkgbase = \(.\+\)$:\1:p' '.SRCINFO')" fi if [ "${#_var_pkgnames[@]}" -eq 0 ]; then IFS=$'\n' read -r -d '' -a _var_pkgnames < <(sed -ne 's:^pkgname = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : fi if [ "${#_var_makedepends[@]}" -eq 0 ]; then IFS=$'\n' read -r -d '' -a _var_makedepends < <(sed -ne 's:^\tmakedepends = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : fi if [ "${#_var_depends[@]}" -eq 0 ]; then IFS=$'\n' read -r -d '' -a _var_depends < <(sed -ne 's:^\tdepends = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : fi if [ "${#_var_conflicts[@]}" -eq 0 ]; then IFS=$'\n' read -r -d '' -a _var_conflicts < <(sed -ne 's:^\tconflicts = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : fi if [ "${#_var_provides[@]}" -eq 0 ]; then IFS=$'\n' read -r -d '' -a _var_provides < <(sed -ne 's:^\tprovides = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : fi IFS=$'\n' read -r -d '' -a _var_pkgverold < <(sed -ne 's:^\tpkgver = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : local _var_srcfiles1=() local _var_srcfile1 IFS=$'\n' read -r -d '' -a _var_srcfiles1 < <(sed -ne 's:^\s\+source[^ =]* = \(.\+\)$:\1:p' '.SRCINFO'; echo -n $'\0') || : if [ "${#_var_srcfiles1[@]}" -ne 0 ]; then for _var_srcfile1 in "${_var_srcfiles1[@]}"; do if ! _fn_find_in_srcfiles "${_var_srcfile1}"; then _var_srcfiles+=("${_var_srcfile1}") fi done fi unset _var_srcfiles1 unset _var_srcfile1 fi if [ ! -z "${_var_install}" ]; then _var_644["${_var_install}"]=' ' # Why is this a space and not blank? See _var_644 above. if _fn_find_in_srcfiles "${_var_install}"; then echo "The install file ${_var_install} should be removed from the source array." [ "${returnv}" -ge 2 ] || returnv=2 fi if [ -s "${_var_install}" ]; then local _var_forbidgrep for _var_forbidgrep in "${_var_forbidgreps[@]}"; do _var_forbidgrep="${_var_forbidgrep//%/q}" if ! eval "${_var_forbidgrep#*|} <(sed -e '/^[ \t]*#/d' '${_var_install}')"; then echo "Warning: The install file ${_var_install} has some forbidden text: ${_var_forbidgrep%%|*}" [ "${returnv}" -ge 1 ] || returnv=1 fi done unset _var_forbidgrep fi fi unset _var_forbidgreps if [ ! -z "${_var_changelog}" ]; then _var_644["${_var_changelog}"]=' ' # Why is this a space? See above. if _fn_find_in_srcfiles "${_var_changelog}"; then echo "The changelog file ${_var_changelog} should be removed from the source array." [ "${returnv}" -ge 2 ] || returnv=2 fi fi # The base is repeated in all the names. The code is here in case we find a case where it's not # The pkgbase is needed elsewhere #if [ "${_var_pkgbase}" != "${_var_pkgbase,,}" ]; then # echo "AUR permits only lowercase package base names: ${_var_pkgbase}" # [ "${returnv}" -ge 2 ] || returnv=2 #fi if [ ! -z "${_var_pkgbase}" ] && [ "${_var_pkgbase}" != "${_var_pwdbn}" ]; then echo '******************************************************' echo "The folder name does not match the pkgbase/pkgname." echo "pkgbase: ${_var_pkgbase}" echo "Folder: ${_var_pwdbn}" echo '******************************************************' [ "${returnv}" -ge 3 ] || returnv=3 fi if [ "${#_var_pkgnames[@]}" -ne 0 ]; then local _var_pkgname for _var_pkgname in "${_var_pkgnames[@]}"; do if [ "${_var_pkgname}" != "${_var_pkgname,,}" ]; then echo "AUR permits only lowercase package names: ${_var_pkgname}" [ "${returnv}" -ge 2 ] || returnv=2 fi done unset _var_pkgname fi unset _var_install unset _var_changelog if [ "${#_var_pkgrelnew[@]}" -gt 1 ]; then echo 'Warning: a package with multiple pkgrel is unexpected. If this is valid please report to the author so this program can be improved.' [ "${returnv}" -ge 1 ] || returnv=1 fi # These are arrays but we'll only check the first one. if [ ! -z "${_var_pkgrelnew:-}" ] && [[ "${_var_pkgrelnew}" != [1-9] ]]; then echo 'pkgrel must be a digit from 1 to 9' [ "${returnv}" -ge 2 ] || returnv=2 fi if [ ! -z "${_var_pkgvernew:-}" ]; then if [ ! -z "${_var_pkgverold:-}" ] && [ ! -z "${_var_pkgrelnew:-}" ] && [ "${_var_pkgvernew}" != "${_var_pkgverold}" ] && [ "${_var_pkgrelnew}" != '1' ]; then echo 'When the version changes the pkgrel must be set back to 1' [ "${returnv}" -ge 2 ] || returnv=2 fi if [ "${_opt_PEDANTIC}" -ge 3 ]; then if [[ "${_var_pkgvernew:-}" != [0-9]* ]]; then echo 'Warning: version numbers typically should not start with a letter' [ "${returnv}" -ge 1 ] || returnv=1 fi else if [[ "${_var_pkgvernew:-}" == [vV][0-9]* ]]; then echo 'Warning: version numbers typically should not start with a v' [ "${returnv}" -ge 1 ] || returnv=1 fi fi fi unset _var_pkgvernew unset _var_pkgverold # Generate specific arrays from _var_srcfiles. Note that since we're mean and read both .SRCINFO, there may be false dups. local _var_srclinks=() # Sometimes this contains false dups. I don't care since it's not used for anything yet. local _var_srcfileswantgit=() local _var_srcfilesdontwantgit=() local _var_srcvcs=0 local _var_wantmakedepends=() if [ "${#_var_srcfiles[@]}" -ne 0 ]; then local _var_srcfile local _var_idx local _var_srclink for _var_idx in "${!_var_srcfiles[@]}"; do _var_srcfile="${_var_srcfiles[${_var_idx}]}" _var_srclink='' if [ "${_var_srcfile/::/}" != "${_var_srcfile}" ]; then #_var_srclink="${_var_srcfile#*::}" # foo::git:// #case "${_var_srclink}" in #bzr*|git*|hg*|svn*)_var_srcvcs=1;; # from makepkg #esac #_var_srcfile="${_var_srcfile%%::*}" # makepkg does it this way case "${_var_srcfile#*::}" in bzr*|git*|hg*|svn*);; # from makepkg, name is only useful if not before ::vcs *) _var_srcfilesdontwantgit+=("${_var_srcfile%%::*}");; esac _var_srcfiles[${_var_idx}]="${_var_srcfile}" _var_srcfile="${_var_srcfile#*::}" fi if [ "${_var_srcfile/:\/\//}" != "${_var_srcfile}" ]; then # it's a link :// _var_srclink="${_var_srcfile}" # We don't about what's on the end here case "${_var_srclink}" in bzr*)_var_srcvcs=1; ! _fn_find_in_wantmakedepends 'bzr|bzr' && _var_wantmakedepends+=('bzr|bzr');; git*)_var_srcvcs=1; ! _fn_find_in_wantmakedepends 'git|git' && _var_wantmakedepends+=('git|git');; hg*) _var_srcvcs=1; ! _fn_find_in_wantmakedepends 'hg|mercurial' && _var_wantmakedepends+=('hg|mercurial');; svn*)_var_srcvcs=1; ! _fn_find_in_wantmakedepends 'svn|subversion' && _var_wantmakedepends+=('svn|subversion');; *) _var_srcfile="${_var_srcfile##*/}" # bash string version of basename _var_srcfilesdontwantgit+=("${_var_srcfile}") _var_srcfiles[${_var_idx}]="${_var_srcfile}" esac else _var_srcfileswantgit+=("${_var_srcfile}") fi if [ ! -z "${_var_srclink}" ]; then _var_srclinks+=("${_var_srclink}") fi ! _fn_find_in_allfiles "${_var_srcfile}" && _var_allfiles+=("${_var_srcfile}") #echo "${_var_srcfile}" done unset _var_srcfile unset _var_idx unset _var_srclink if [ -f 'PKGBUILD' ] && [ "${_var_srcvcs}" -ne 0 ]; then if ! grep -ql '^pkgver\s*(' 'PKGBUILD'; then echo 'Warning: A pkgver() function is usually useful for packages with vcs sources.' [ "${returnv}" -ge 1 ] || returnv=1 fi fi else echo 'Warning: .SRCINFO has no source files' [ "${returnv}" -ge 1 ] || returnv=1 fi unset _var_srcvcs unset _var_srclinks # This went through 3 revisions. # 1) Run through git ls-files with FSA. Unmaintainable mess. # 2) Run through git status --porcelain with FSA. Only shows diffs and still unmaintainable. # 3) Now it's a series of simple set operations. The sets can be verified with set > tmpfile. if _fn_find_in_depends 'gcc'; then echo 'Warning: gcc should probably be moved from depends to makedepends.' [ "${returnv}" -ge 1 ] || returnv=1 fi if [ "${#_var_wantmakedepends[@]}" -ne 0 ]; then local _var_wantmakedepend for _var_wantmakedepend in "${_var_wantmakedepends[@]}"; do _var_wantmakedepend="${_var_wantmakedepend#*|}" if ! _fn_find_in_makedepends "${_var_wantmakedepend}" && ! _fn_find_in_depends "${_var_wantmakedepend}"; then echo "For this vcs package '${_var_wantmakedepend}' must be added to makedepends or possibly depends." [ "${returnv}" -ge 2 ] || returnv=2 fi done if [ "${#_var_wantmakedepends[@]}" -eq 1 ]; then # We don't have an opinion if there are 2 or more different vcs sources _var_wantmakedepend="${_var_wantmakedepends[0]}" _var_wantmakedepend="${_var_wantmakedepend%%|*}" local _var_wrongext='' case "${_var_pkgbase}" in *-bzr) [ "${_var_wantmakedepend}" != 'bzr' ] && _var_wrongext='-bzr';; *-git) [ "${_var_wantmakedepend}" != 'git' ] && _var_wrongext='-git';; *-hg) [ "${_var_wantmakedepend}" != 'hg' ] && _var_wrongext='-hg';; *-svn) [ "${_var_wantmakedepend}" != 'svn' ] && _var_wrongext='-svn';; esac if [ ! -z "${_var_wrongext}" ]; then echo "${_var_wrongext} is the wrong suffix for your vcs package. Try -${_var_wantmakedepend}" [ "${returnv}" -ge 2 ] || returnv=2 fi unset _var_wrongext fi unset _var_wantmakedepend fi case "${_var_pkgbase}" in *-bzr|*-git|*-hg|*-svn) if ! _fn_find_in_conflicts "${_var_pkgbase%-*}" || ! _fn_findver_in_provides "${_var_pkgbase%-*}"; then echo 'Warning: most vcs packages should have a conflicts and provides' [ "${returnv}" -ge 1 ] || returnv=1 fi if ! _fn_findst_in_provides "${_var_pkgbase%-*}="; then echo 'Warning: vcs packages cannot satisfy versioned dependencies without a provides=version' [ "${returnv}" -ge 1 ] || returnv=1 fi if [ "${_var_pkgrelnew:-}" != '1' ]; then echo 'Warning: vcs packages usually have pkgrel=1' [ "${returnv}" -ge 1 ] || returnv=1 fi ;; esac unset _var_pkgbase unset _var_pkgrelnew local _var_reqfile for _var_reqfile in "${!_var_644[@]}"; do ! _fn_find_in_allfiles "${_var_reqfile}" && _var_allfiles+=("${_var_reqfile}") # This probably isn't necessary ! _fn_find_in_srcfiles "${_var_reqfile}" && _var_srcfiles+=("${_var_reqfile}") # This probably isn't necessary ! _fn_find_in_srcfileswantgit "${_var_reqfile}" && _var_srcfileswantgit+=("${_var_reqfile}") if [ ! -s "${_var_reqfile}" ]; then echo "Required file ${_var_reqfile} doesn't exist or blank.${_var_644[${_var_reqfile}]}" [ "${returnv}" -ge 2 ] || returnv=2 elif [ "$(stat -c '%a' "${_var_reqfile}")" != '644' ]; then echo "File ${_var_reqfile} must have perms 644. Try: chmod 644 \"${_var_reqfile}\"" [ "${returnv}" -ge 2 ] || returnv=2 fi if ! _fn_find_in_gitfiles "${_var_reqfile}" && ! _fn_find_in_gitadds "${_var_reqfile}"; then echo "Required file ${_var_reqfile} not in git. Try: git add \"${_var_reqfile}\"" [ "${returnv}" -ge 2 ] || returnv=2 _var_gitadds+=("${_var_reqfile}") fi done unset _var_reqfile if [ "${#_var_srcfileswantgit[@]}" -ne 0 ]; then local _var_srcfile for _var_srcfile in "${_var_srcfileswantgit[@]}"; do if [ "${_var_644[${_var_srcfile}]:-xyzzy}" = 'xyzzy' ]; then if ! _fn_find_in_gitfiles "${_var_srcfile}" && ! _fn_find_in_gitadds "${_var_srcfile}"; then echo "Source file ${_var_srcfile} not in git. Try: git add \"${_var_srcfile}\"" [ "${returnv}" -ge 2 ] || returnv=2 _var_gitadds+=("${_var_srcfile}") fi if [ ! -f "${_var_srcfile}" ]; then echo "Source file ${_var_srcfile} doesn't exist or blank." [ "${returnv}" -ge 2 ] || returnv=2 fi fi done unset _var_srcfile fi if [ "${#_var_gitfiles[@]}" -ne 0 ]; then local _var_gitfile for _var_gitfile in "${_var_gitfiles[@]}"; do if _fn_find_in_srcfilesdontwantgit "${_var_gitfile}"; then echo "Downloaded file ${_var_gitfile} should be removed from git. Try: git rm --cached \"${_var_gitfile}\"" [ "${returnv}" -ge 2 ] || returnv=2 elif ! _fn_find_in_srcfileswantgit "${_var_gitfile}"; then echo "Warning: File ${_var_gitfile} in git not in PKGBUILD. Maybe try: git rm --cached \"${_var_gitfile}\"" [ "${returnv}" -ge 1 ] || returnv=1 fi done unset _var_gitfile fi local _var_allfile local _var_perms for _var_allfile in "${_var_allfiles[@]}"; do if [ -f "${_var_allfile}" ]; then if [ -z "${_var_644[${_var_allfile}]:-}" ]; then _var_perms="$(stat -c '%a' "${_var_allfile}")" if [ "${_var_perms}" != '755' ] && [ "${_var_perms}" != '644' ]; then if _fn_find_in_srcfileswantgit "${_var_allfile}"; then echo "File ${_var_allfile} must have perms 644 or 755" else echo "File ${_var_allfile} should have perms 644 or 755" fi [ "${returnv}" -ge 2 ] || returnv=2 fi fi if [ "$(stat -c '%U' "${_var_allfile}")" = 'root' ]; then echo "File ${_var_allfile} owned by root" [ "${returnv}" -ge 2 ] || returnv=2 fi fi done unset _var_perms unset _var_allfile #if ... We can do this if we decide to decode git-status # echo "${_var_gitfile} has been modified. Try: 'git add ${_var_gitfile}' or 'git add -u'" # [ "${returnv}" -ge 2 ] || returnv=2 #el # alas, git ls-files does not show us files that have been "git rm" but not pushed if [ "${#_var_gitadds[@]}" -gt 0 ]; then #_var_gitadds+=($'foo\t\n\r\lbar foobar' $'foo bar') # Test out %q echo -n 'Found: git add' builtin printf ' %q' "${_var_gitadds[@]}" echo '' fi local _var_gitmodified _var_gitmodified="$(git ls-files -m)" if [ "${_opt_PEDANTIC}" -ge 2 -o "${_opt_ALL}" -eq 0 ] && [ ! -z "${_var_gitmodified}" ]; then echo "Warning: There are modified files. Try 'git status' and maybe 'git add -u'" [ "${returnv}" -ge 1 ] || returnv=1 fi local _var_gitadded _var_gitadded="$(git status --porcelain | grep '^M ')" || : local _var_returnvbeforestaged="${returnv}" if [ "${_opt_PEDANTIC}" -ge 2 -o "${_opt_ALL}" -eq 0 ] && git status --porcelain | grep -qv '^?\|^ M'; then echo "Warning: There are staged files. Try 'git status' and maybe commit and push" [ "${returnv}" -ge 1 ] || returnv=1 fi set +e local _var_push _var_push="$(git cherry -v 2>/dev/null)" # local cannot be on this line or we lose the return value if [ $? -ne 0 ]; then # We want to only generate this warning near the commit instead of after every step. if [ "${_var_returnvbeforestaged}" -le 1 ]; then if [ "${returnv}" -eq 0 ]; then echo "Warning: You have not made your first push to create the ${_opt_AUR4^^} package. Maybe try 'git push origin master'" else echo "Warning: You have not made your first push to create the ${_opt_AUR4^^} package. Maybe try 'git commit -m \"Initial Import\"' and 'git push origin master'" fi [ "${returnv}" -ge 1 ] || returnv=1 fi set +e local _var_revlist _var_revlist="$(git rev-list HEAD --count 2>/dev/null)" if [ $? -eq 0 ] && [ ! -z "${_var_revlist}" ] && [ "${_var_revlist}" -ge 2 ]; then # git revert is worthless as it reverts in the forward direction. We need to go two steps in reverse to before HEAD. echo "There are multiple commits on an empty repository. This may not do what you want. It seems that git cannot reset before your first commit HEAD back to an empty repository. You may need to rm -rf '.git' and clone again as instructed by $(basename "$0")." [ "${returnv}" -ge 2 ] || returnv=2 fi set -e elif [ ! -z "${_var_push}" ]; then # Unfortunately this does not detect multiple commits before the first commit is made if [ "${#_var_gitadds[@]}" -gt 0 ] || [ ! -z "${_var_gitmodified}" ] || [ ! -z "${_var_gitadded}" ] || [ "$(git log @{push}.. | grep -c '^commit')" -ge 2 ]; then # http://stackoverflow.com/questions/927358/how-do-you-undo-the-last-commit\ echo "There are modifications after a commit. A push or another commit may not do what you want. Maybe try multiple times: git reset --soft 'HEAD~1'; git status" [ "${returnv}" -ge 2 ] || returnv=2 else echo "Warning: There are commits not pushed. Try 'git cherry -v' and maybe push" [ "${returnv}" -ge 1 ] || returnv=1 fi fi set -e unset _var_push unset _var_returnvbeforestaged unset _var_gitmodified unset _var_gitadded local _var_remoteurl _var_remoteurl="$(git ls-remote --get-url)" local _var_remoteurlbn _var_remoteurlbn="$(basename "${_var_remoteurl}" ".git")" if ! grep -qlF $'ssh://\nssh+git://' <<< "${_var_remoteurl}"; then echo "Warning: You don't have write access to this package so you can't fix these problems." # I see no reason to report this as a warning. fi if [ "${_var_remoteurl//aur4.archlinux.org/}" != "${_var_remoteurl}" ]; then if [ "${_opt_ALL}" -ne 0 ]; then echo "Warning: You should change your origin away from AUR4. Try: pushd '${_var_pwdbn}' >/dev/null && { git remote set-url origin '${_var_remoteurl//aur4.archlinux.org/aur.archlinux.org}'; popd >/dev/null; }" else echo "Warning: You should change your origin away from AUR4. Try: git remote set-url origin '${_var_remoteurl//aur4.archlinux.org/aur.archlinux.org}'" fi [ "${returnv}" -ge 1 ] || returnv=1 fi # git-clone from AUR should block the cloning of new repository packages in core/extra, but it doesn't. if [ "${_var_remoteurlbn}" != "${_var_pwdbn}" ]; then echo '******************************************************' echo "The git package name doesn't match the folder name. Did you clone the wrong package?" git ls-remote --get-url echo "Folder: ${_var_pwdbn}" echo '******************************************************' [ "${returnv}" -ge 3 ] || returnv=3 fi unset _var_remoteurl unset _var_remoteurlbn #set > x # look for variables we forgot to unset else echo "${_var_pwdbn} is not an ${_opt_AUR4} package." if [ "${_var_pwd}" = ~/build ]; then echo 'This looks like a build folder.' echo "To check all with write access try: $0 -a | less" echo "To check just one try: cd foo; $(basename "$0")" elif [ -d ~/build ]; then echo 'Run this in your build folder to check many packages.' fi if [ -f 'PKGBUILD' ]; then # rm -rf a folder that likely exists is quite dangerous so we do 3 safety steps: stop if files and folderse aren't right, set -e to crash on any error, and clone to a temp folder. # To keep things safe does make the command a bit complex. I'm hoping this is histexpand safe. echo 'You can easily upgrade to read or write access without losing your files with:' echo "Write: test -s PKGBUILD -a '!' -d .git -a '!' -d '${_var_pwdbn}.aurtmp' && { git clone 'ssh://aur@${_opt_AUR4}.archlinux.org/${_var_pwdbn}.git' '${_var_pwdbn}.aurtmp' && mv '${_var_pwdbn}.aurtmp/.git' . && rm -rf '${_var_pwdbn}.aurtmp'} || echo 'How about NO!'" echo "Read: test -s PKGBUILD -a '!' -d .git -a '!' -d '${_var_pwdbn}.aurtmp' && { git clone 'https://${_opt_AUR4}.archlinux.org/${_var_pwdbn}.git/' '${_var_pwdbn}.aurtmp' && mv '${_var_pwdbn}.aurtmp/.git' . && rm -rf '${_var_pwdbn}.aurtmp'} || echo 'How about NO!'" fi [ "${returnv}" -ge 3 ] || returnv=3 fi local _var_file for _var_file in '.MTREE' '.AURINFO' '.PKGINFO' '.INSTALL'; do if [ -f "${_var_file}" ]; then echo "Warning: ${_var_file} should be deleted" [ "${returnv}" -ge 1 ] || returnv=1 fi done } _fn_usage() { local _var_BN _var_BN="$(basename "$0")" cat << EOF git-aurcheck ${_opt_VERSION} (C)2015 by severach for Arch Linux (GPL3+) -h crude help -a from ~/build folder, check all packages with write access -x= exclude package folders from -a scan. Ignored without -a. package[s] scan only specific packages. Exclusions dominate. -p pedantic, adds extra checks. Up to thrice for maximum pedantry. -v check for new version with PKGBUILD _verwatch. -t= change to target folder before starting. Useful for cron. -V= elevate new version warning to desired exit code. Numbers only. To check for common problems in a package folder: cd ~/build/foo ${_var_BN} To check for all problems and new versions in a package folder: ${_var_BN} -pppv To check for problems in all packages with git ssh write access except foo,bar cd ~/build ${_var_BN} -a -x foo -x bar To scan for updates in packages foo and bar ${_var_BN} -t $HOME/user/build -Vva foo bar EOF } _opt_ALL=0 #_opt_FORCE=0 # If ever implemented, would automatically perform as many safe changes as possible. _opt_PEDANTIC=0 _opt_VERSION=0 _opt_EXCLUDES=() _opt_VEREXITCODE=1 _opt_DEBUG='' _opt_DEBUGVER=0 while getopts ':hafpvx:t:V:D:' _var_opt; do case "${_var_opt}" in h) _fn_usage; exit 0;; a) _opt_ALL=1;; # f) _opt_FORCE=1;; # cannot be specified with -a p) _opt_PEDANTIC=$((_opt_PEDANTIC + 1));; v) _opt_VERSION=1;; x) _opt_EXCLUDES+=("${OPTARG}");; t) cd "${OPTARG}";; # from install -t V) _opt_VEREXITCODE="${OPTARG}";; D) _opt_DEBUG="${OPTARG}";; :) echo "$(basename "$0"): Option '-${OPTARG}' requires an argument" >&2 exit 126;; *) echo "$(basename "$0"): Invalid option '-${OPTARG}'" >&2 _fn_usage; exit 126;; esac done unset _var_opt _opt_POSARGS=("${@: ${OPTIND}}") test "${_opt_DEBUG/v/}" != "${_opt_DEBUG}" && _opt_DEBUGVER=1 || _opt_DEBUGVER=0 # -Dv returnv=0 if [ "${_opt_ALL}" -ne 0 ]; then if [ -e 'PKGBUILD' ] ;then echo 'This looks like a package folder. Maybe you want to leave -a off.' exit 126 elif [ "${#_opt_POSARGS[@]}" -ne 0 ]; then # _opt_FORCE=0 for builddir in "${_opt_POSARGS[@]}"; do if [ -s "${builddir}/.git/config" ] && ! _fn_find_in_EXCLUDES "${builddir}" && pushd "${builddir}" >/dev/null; then echo "Checking ${builddir}" _fn_aurcheck popd >/dev/null fi done else # _opt_FORCE=0 for builddir in *; do # It doesn't do any good to check packages that you can't fix because they aren't yours. # We consider writable ssh:// to be yours and read only http:// to not be yours. # Use https:// to clone packages you don't have write access to. # You can fix errant clones with the https url on the package page. # Example: # cd ~/build/foo # git ls-remote --get-url # git remote set-url origin "https://aur.archlinux.org/$(basename "$(pwd)").git/" # git remote show origin -n if [ -s "${builddir}/.git/config" ] && ! _fn_find_in_EXCLUDES "${builddir}" && grep -qlF $'url = ssh://\nurl = ssh+git://' "${builddir}/.git/config" && pushd "${builddir}" >/dev/null; then echo "Checking ${builddir}" _fn_aurcheck popd >/dev/null fi done fi else _fn_aurcheck fi exit "${returnv}"