diff options
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | PKGBUILD.proto | 48 | ||||
-rw-r--r-- | arch-nspawn.in | 29 | ||||
-rw-r--r-- | archbuild.in | 9 | ||||
-rw-r--r-- | archco.in | 2 | ||||
-rw-r--r-- | archrelease.in | 6 | ||||
-rw-r--r-- | archrm.in | 2 | ||||
-rw-r--r-- | bash_completion.in | 7 | ||||
-rw-r--r-- | checkpkg.in | 19 | ||||
-rw-r--r-- | commitpkg.in | 47 | ||||
-rw-r--r-- | crossrepomove.in | 16 | ||||
-rw-r--r-- | find-libdeps.in | 14 | ||||
-rw-r--r-- | finddeps.in | 11 | ||||
-rw-r--r-- | lddd.in | 24 | ||||
-rw-r--r-- | lib/archroot.sh | 67 | ||||
-rw-r--r-- | lib/common.sh | 129 | ||||
-rw-r--r-- | lib/valid-tags.sh | 4 | ||||
-rw-r--r-- | makechrootpkg.in | 234 | ||||
-rw-r--r-- | makepkg-i686.conf | 3 | ||||
-rw-r--r-- | makepkg-x86_64.conf | 3 | ||||
-rw-r--r-- | mkarchroot.in | 17 | ||||
-rw-r--r-- | rebuildpkgs.in | 23 |
22 files changed, 433 insertions, 290 deletions
@@ -1,4 +1,4 @@ -V=20160528 +V=20170320 PREFIX = /usr/local @@ -74,7 +74,7 @@ edit = sed -e "s|@pkgdatadir[@]|$(DESTDIR)$(PREFIX)/share/devtools|g" %: %.in Makefile lib/common.sh @echo "GEN $@" @$(RM) "$@" - @m4 -P $@.in | $(edit) >$@ + @{ echo -n 'm4_changequote([[[,]]])'; cat $@.in; } | m4 -P | $(edit) >$@ @chmod a-w "$@" @chmod +x "$@" @bash -O extglob -n "$@" @@ -114,5 +114,8 @@ dist: upload: scp devtools-$(V).tar.gz devtools-$(V).tar.gz.sig repos.archlinux.org:/srv/ftp/other/devtools/ -.PHONY: all clean install uninstall dist upload +check: $(BINPROGS) bash_completion makepkg-x86_64.conf PKGBUILD.proto + shellcheck $^ +.PHONY: all clean install uninstall dist upload check +.DELETE_ON_ERROR: diff --git a/PKGBUILD.proto b/PKGBUILD.proto new file mode 100644 index 0000000..e8690e4 --- /dev/null +++ b/PKGBUILD.proto @@ -0,0 +1,48 @@ +#!/hint/bash +# shellcheck disable=2034 + +# This is an example PKGBUILD file, so that shellcheck can know what +# variables to expect be set after including a PKGBUILD. + +# Maintainer: Your Name <youremail@domain.com> +pkgname=NAME +pkgver=VERSION +pkgrel=1 +epoch= +pkgdesc="" +arch=() +url="" +license=('GPL') +groups=() +depends=() +makedepends=() +checkdepends=() +optdepends=() +provides=() +conflicts=() +replaces=() +backup=() +options=() +install= +changelog= +source=("$pkgname-$pkgver.tar.gz" + "$pkgname-$pkgver.patch") +noextract=() +md5sums=() +validpgpkeys=() + +prepare() { + : +} + +build() { + : +} + +check() { + : +} + +package() { + : +} diff --git a/arch-nspawn.in b/arch-nspawn.in index adee72e..0b0dc68 100644 --- a/arch-nspawn.in +++ b/arch-nspawn.in @@ -11,8 +11,7 @@ # GNU General Public License for more details. m4_include(lib/common.sh) - -CHROOT_VERSION='v3' +m4_include(lib/archroot.sh) working_dir='' @@ -32,8 +31,6 @@ usage() { exit 1 } -orig_argv=("$@") - while getopts 'hC:M:c:f:s' arg; do case "$arg" in C) pac_conf="$OPTARG" ;; @@ -45,10 +42,10 @@ while getopts 'hC:M:c:f:s' arg; do *) error "invalid argument '%s'" "$arg"; usage ;; esac done -shift $(($OPTIND - 1)) +shift $((OPTIND - 1)) (( $# < 1 )) && die 'You must specify a directory.' -check_root "$0" "${orig_argv[@]}" +check_root working_dir=$(readlink -f "$1") shift 1 @@ -61,7 +58,9 @@ else cache_dirs=("$cache_dir") fi +# shellcheck disable=2016 host_mirror=$(pacman --cachedir /doesnt/exist -Sddp extra/devtools 2>/dev/null | sed -r 's#(.*/)extra/os/.*#\1$repo/os/$arch#') +# shellcheck disable=2016 [[ $host_mirror == *file://* ]] && host_mirror_path=$(echo "$host_mirror" | sed -r 's#file://(/.*)/\$repo/os/\$arch#\1#g') # {{{ functions @@ -69,13 +68,13 @@ build_mount_args() { declare -g mount_args=() if [[ -n $host_mirror_path ]]; then - mount_args+=(--bind-ro="$host_mirror_path") + mount_args+=("--bind-ro=$host_mirror_path") fi - mount_args+=(--bind="${cache_dirs[0]}") + mount_args+=("--bind=${cache_dirs[0]}") - for cache_dir in ${cache_dirs[@]:1}; do - mount_args+=(--bind-ro="$cache_dir") + for cache_dir in "${cache_dirs[@]:1}"; do + mount_args+=("--bind-ro=$cache_dir") done } @@ -83,8 +82,8 @@ copy_hostconf () { cp -a /etc/pacman.d/gnupg "$working_dir/etc/pacman.d" echo "Server = $host_mirror" >"$working_dir/etc/pacman.d/mirrorlist" - [[ -n $pac_conf ]] && cp $pac_conf "$working_dir/etc/pacman.conf" - [[ -n $makepkg_conf ]] && cp $makepkg_conf "$working_dir/etc/makepkg.conf" + [[ -n $pac_conf ]] && cp "$pac_conf" "$working_dir/etc/pacman.conf" + [[ -n $makepkg_conf ]] && cp "$makepkg_conf" "$working_dir/etc/makepkg.conf" local file for file in "${files[@]}"; do @@ -92,7 +91,7 @@ copy_hostconf () { cp -T "$file" "$working_dir$file" done - sed -r "s|^#?\\s*CacheDir.+|CacheDir = $(echo -n ${cache_dirs[@]})|g" -i "$working_dir/etc/pacman.conf" + sed -r "s|^#?\\s*CacheDir.+|CacheDir = $(echo -n "${cache_dirs[@]}")|g" -i "$working_dir/etc/pacman.conf" } # }}} @@ -101,7 +100,7 @@ umask 0022 # Sanity check if [[ ! -f "$working_dir/.arch-chroot" ]]; then die "'%s' does not appear to be an Arch chroot." "$working_dir" -elif [[ $(cat "$working_dir/.arch-chroot") != $CHROOT_VERSION ]]; then +elif [[ $(cat "$working_dir/.arch-chroot") != "$CHROOT_VERSION" ]]; then die "chroot '%s' is not at version %s. Please rebuild." "$working_dir" "$CHROOT_VERSION" fi @@ -109,7 +108,7 @@ build_mount_args cache_dirs+=('/repo/') copy_hostconf -eval $(grep '^CARCH=' "$working_dir/etc/makepkg.conf") +eval "$(grep '^CARCH=' "$working_dir/etc/makepkg.conf")" case "$CARCH" in armv7h) CARCH=armv7l;; esac diff --git a/archbuild.in b/archbuild.in index 60e7cec..8339aef 100644 --- a/archbuild.in +++ b/archbuild.in @@ -2,6 +2,7 @@ # License: Unspecified m4_include(lib/common.sh) +m4_include(lib/archroot.sh) base_packages=(base-devel) makechrootpkg_args=(-c -n) @@ -30,8 +31,6 @@ usage() { exit 1 } -orig_argv=("$@") - while getopts 'hcr:' arg; do case "${arg}" in c) clean_first=true ;; @@ -40,7 +39,7 @@ while getopts 'hcr:' arg; do esac done -check_root "$0" "${orig_argv[@]}" +check_root # Pass all arguments after -- right to makepkg makechrootpkg_args+=("${@:$OPTIND}") @@ -54,9 +53,7 @@ if ${clean_first} || [[ ! -d "${chroots}/${repo}-${arch}" ]]; then lock 9 "$copy.lock" "Locking chroot copy '%s'" "$copy" - if [[ "$(stat -f -c %T "${copy}")" == btrfs ]]; then - { type -P btrfs && btrfs subvolume delete "${copy}"; } &>/dev/null - fi + subvolume_delete_recursive "${copy}" rm -rf --one-file-system "${copy}" done lock_close 9 @@ -6,7 +6,7 @@ m4_include(lib/common.sh) scriptname=${0##*/} if [[ -z $1 ]]; then - echo 'Usage: '$scriptname' <package name>...' + printf 'Usage: %s <package name>...\n' "$scriptname" exit 1 fi diff --git a/archrelease.in b/archrelease.in index 3b11652..6b4f1be 100644 --- a/archrelease.in +++ b/archrelease.in @@ -38,7 +38,7 @@ trunk=${PWD##*/} # Normally this should be trunk, but it may be something # such as 'gnome-unstable' IFS='/' read -r -d '' -a parts <<< "$PWD" -if [[ "${parts[@]:(-2):1}" == "repos" ]]; then +if [[ "${parts[*]:(-2):1}" == "repos" ]]; then die 'archrelease: Should not be in repos dir (try from trunk/)' fi unset parts @@ -67,14 +67,14 @@ for tag in "$@"; do while read -r file; do trash+=("repos/$tag/$file") done < <(svn ls "repos/$tag") - [[ $trash ]] && svn rm -q "${trash[@]/%/@}" + [[ ${#trash[@]} == 0 ]] || svn rm -q "${trash[@]/%/@}" else mkdir -p "repos/$tag" svn add --parents -q "repos/$tag" fi # copy all files at once from trunk to the subdirectory in repos/ - svn copy -q -r HEAD ${known_files[@]/#/$trunk/} "repos/$tag/" + svn copy -q -r HEAD "${known_files[@]/#/$trunk/}" "repos/$tag/" stat_done done @@ -13,4 +13,4 @@ fi # #popd -rm -rf $1 +rm -rf "$1" diff --git a/bash_completion.in b/bash_completion.in index b9ed69c..9feef74 100644 --- a/bash_completion.in +++ b/bash_completion.in @@ -1,11 +1,12 @@ +#!/hint/bash # License: Unspecified _devtools_compgen() { local i r COMPREPLY=($(compgen -W '$*' -- "$cur")) for ((i=1; i < ${#COMP_WORDS[@]}-1; i++)); do - for r in ${!COMPREPLY[@]}; do - if [[ ${COMP_WORDS[i]} = ${COMPREPLY[r]} ]]; then + for r in "${!COMPREPLY[@]}"; do + if [[ ${COMP_WORDS[i]} = "${COMPREPLY[r]}" ]]; then unset 'COMPREPLY[r]'; break fi done @@ -14,7 +15,7 @@ _devtools_compgen() { _archco_pkg() { _devtools_compgen "$( - \pacman -$1 + command pacman "-$1" )" } diff --git a/checkpkg.in b/checkpkg.in index 6904e32..cfec71e 100644 --- a/checkpkg.in +++ b/checkpkg.in @@ -28,14 +28,19 @@ fi # Source makepkg.conf; fail if it is not found if [[ -r '/etc/makepkg.conf' ]]; then + # shellcheck source=makepkg-x86_64.conf source '/etc/makepkg.conf' else die '/etc/makepkg.conf not found!' fi # Source user-specific makepkg.conf overrides -if [[ -r ~/.makepkg.conf ]]; then - source ~/.makepkg.conf +if [[ -r "${XDG_CONFIG_HOME:-$HOME/.config}/pacman/makepkg.conf" ]]; then + # shellcheck source=/dev/null + source "${XDG_CONFIG_HOME:-$HOME/.config}/pacman/makepkg.conf" +elif [[ -r "$HOME/.makepkg.conf" ]]; then + # shellcheck source=/dev/null + source "$HOME/.makepkg.conf" fi if [[ ! -f PKGBUILD ]]; then @@ -44,8 +49,9 @@ if [[ ! -f PKGBUILD ]]; then exit 1 fi +# shellcheck source=PKGBUILD.proto . ./PKGBUILD -if [[ $arch == 'any' ]]; then +if [[ ${arch[0]} == 'any' ]]; then CARCH='any' fi @@ -60,15 +66,12 @@ for _pkgname in "${pkgname[@]}"; do ln -s "$pkgfile" "$TEMPDIR" - pkgurl=$(pacman -Spdd --print-format '%l' --noconfirm "$_pkgname") - - if [[ $? -ne 0 ]]; then + pkgurl=$(pacman -Spdd --print-format '%l' --noconfirm "$_pkgname") || die "Couldn't download previous package for %s." "$_pkgname" - fi oldpkg=${pkgurl##*://*/} - if [[ ${oldpkg##*/} = ${pkgfile##*/} ]]; then + if [[ ${oldpkg##*/} = "${pkgfile##*/}" ]]; then die "The built package (%s) is the one in the repo right now!" "$_pkgname" fi diff --git a/commitpkg.in b/commitpkg.in index d31f6ba..53b6612 100644 --- a/commitpkg.in +++ b/commitpkg.in @@ -5,14 +5,19 @@ m4_include(lib/common.sh) # Source makepkg.conf; fail if it is not found if [[ -r '/etc/makepkg.conf' ]]; then + # shellcheck source=makepkg-x86_64.conf source '/etc/makepkg.conf' else die '/etc/makepkg.conf not found!' fi # Source user-specific makepkg.conf overrides -if [[ -r ~/.makepkg.conf ]]; then - . ~/.makepkg.conf +if [[ -r "${XDG_CONFIG_HOME:-$HOME/.config}/pacman/makepkg.conf" ]]; then + # shellcheck source=/dev/null + source "${XDG_CONFIG_HOME:-$HOME/.config}/pacman/makepkg.conf" +elif [[ -r "$HOME/.makepkg.conf" ]]; then + # shellcheck source=/dev/null + source "$HOME/.makepkg.conf" fi cmd=${0##*/} @@ -21,6 +26,8 @@ if [[ ! -f PKGBUILD ]]; then die 'No PKGBUILD file' fi +source=() +# shellcheck source=PKGBUILD.proto . ./PKGBUILD pkgbase=${pkgbase:-$pkgname} @@ -48,7 +55,7 @@ done for i in 'changelog' 'install'; do while read -r file; do # evaluate any bash variables used - eval file=\"$(sed "s/^\(['\"]\)\(.*\)\1\$/\2/" <<< "$file")\" + eval "file=\"$(sed "s/^\(['\"]\)\(.*\)\1\$/\2/" <<< "$file")\"" needsversioning+=("$file") done < <(sed -n "s/^[[:space:]]*$i=//p" PKGBUILD) done @@ -62,7 +69,7 @@ if (( ${#needsversioning[*]} )); then (( ${#unversioned[*]} )) && die "%s is not under version control" "${unversioned[@]}" fi -rsyncopts=(-e ssh -p --chmod=ug=rw,o=r -c -h -L --progress --partial -y) +rsyncopts=(-e ssh -p '--chmod=ug=rw,o=r' -c -h -L --progress --partial -y) archreleaseopts=() while getopts ':l:a:s:f' flag; do case $flag in @@ -77,12 +84,12 @@ done shift $(( OPTIND - 1 )) # check packages have the packager field set -for _arch in ${arch[@]}; do +for _arch in "${arch[@]}"; do if [[ -n $commit_arch && ${_arch} != "$commit_arch" ]]; then continue fi - for _pkgname in ${pkgname[@]}; do - fullver=$(get_full_version $_pkgname) + for _pkgname in "${pkgname[@]}"; do + fullver=$(get_full_version "$_pkgname") if pkgfile=$(find_cached_package "$_pkgname" "$_arch" "$fullver"); then if grep -q "packager = Unknown Packager" <(bsdtar -xOqf "$pkgfile" .PKGINFO); then @@ -126,18 +133,18 @@ declare -a uploads declare -a commit_arches declare -a skip_arches -for _arch in ${arch[@]}; do +for _arch in "${arch[@]}"; do if [[ -n $commit_arch && ${_arch} != "$commit_arch" ]]; then - skip_arches+=($_arch) + skip_arches+=("$_arch") continue fi - for _pkgname in ${pkgname[@]}; do - fullver=$(get_full_version $_pkgname) + for _pkgname in "${pkgname[@]}"; do + fullver=$(get_full_version "$_pkgname") if ! pkgfile=$(find_cached_package "$_pkgname" "$fullver" "${_arch}"); then warning "Skipping %s: failed to locate package file" "$_pkgname-$fullver-$_arch" - skip_arches+=($_arch) + skip_arches+=("$_arch") continue 2 fi uploads+=("$pkgfile") @@ -146,9 +153,9 @@ for _arch in ${arch[@]}; do if [[ ! -f $sigfile ]]; then msg "Signing package %s..." "${pkgfile}" if [[ -n $GPGKEY ]]; then - SIGNWITHKEY="-u ${GPGKEY}" + SIGNWITHKEY=(-u "${GPGKEY}") fi - gpg --detach-sign --use-agent --no-armor ${SIGNWITHKEY} "${pkgfile}" || die + gpg --detach-sign --use-agent --no-armor "${SIGNWITHKEY[@]}" "${pkgfile}" || die fi if ! gpg --verify "$sigfile" >/dev/null 2>&1; then die "Signature %s.sig is incorrect!" "$pkgfile" @@ -157,9 +164,9 @@ for _arch in ${arch[@]}; do done done -for _arch in ${arch[@]}; do - if ! in_array $_arch ${skip_arches[@]}; then - commit_arches+=($_arch) +for _arch in "${arch[@]}"; do + if ! in_array "$_arch" "${skip_arches[@]}"; then + commit_arches+=("$_arch") fi done @@ -185,8 +192,8 @@ if [[ "${arch[*]}" == 'any' ]]; then if [[ -d ../repos/$repo-i686 && -d ../repos/$repo-x86_64 ]]; then pushd ../repos/ >/dev/null stat_busy "Removing %s and %s" "$repo-i686" "$repo-x86_64" - svn rm -q $repo-i686 - svn rm -q $repo-x86_64 + svn rm -q "$repo-i686" + svn rm -q "$repo-x86_64" svn commit -q -m "Removed $repo-i686 and $repo-x86_64 for $pkgname" stat_done popd >/dev/null @@ -195,7 +202,7 @@ else if [[ -d ../repos/$repo-any ]]; then pushd ../repos/ >/dev/null stat_busy "Removing %s" "$repo-any" - svn rm -q $repo-any + svn rm -q "$repo-any" svn commit -q -m "Removed $repo-any for $pkgname" stat_done popd >/dev/null diff --git a/crossrepomove.in b/crossrepomove.in index 1be2dc3..b45b8ae 100644 --- a/crossrepomove.in +++ b/crossrepomove.in @@ -6,7 +6,7 @@ m4_include(lib/common.sh) scriptname=${0##*/} if [[ -z $1 ]]; then - echo 'Usage: '$scriptname' [pkgbase]' + printf 'Usage: %s [pkgbase]\n' "$scriptname" exit 1 fi @@ -38,24 +38,26 @@ target_dbscripts="/srv/repos/svn-${target_name}/dbscripts" setup_workdir -pushd $WORKDIR >/dev/null +pushd "$WORKDIR" >/dev/null msg "Downloading sources for %s" "${pkgbase}" svn -q checkout -N "${target_svn}" target_checkout mkdir -p "target_checkout/${pkgbase}/repos" svn -q export "${source_svn}/${pkgbase}/trunk" "target_checkout/${pkgbase}/trunk" || die +# shellcheck source=PKGBUILD.proto . "target_checkout/${pkgbase}/trunk/PKGBUILD" msg "Downloading packages for %s" "${pkgbase}" -for _arch in ${arch[@]}; do +for _arch in "${arch[@]}"; do if [[ "${_arch[*]}" == 'any' ]]; then repo_arch='x86_64' else repo_arch=${_arch} fi - for _pkgname in ${pkgname[@]}; do - fullver=$(get_full_version $_pkgname) + for _pkgname in "${pkgname[@]}"; do + fullver=$(get_full_version "$_pkgname") pkgpath="/srv/ftp/$source_repo/os/$repo_arch/$_pkgname-$fullver-${_arch}.pkg.tar.*" + # shellcheck disable=2029 ssh "$server" "cp $pkgpath staging/$target_repo" || die done done @@ -68,10 +70,12 @@ pushd "target_checkout/${pkgbase}/trunk" >/dev/null archrelease "${arch[@]/#/$target_repo-}" || die popd >/dev/null +# shellcheck disable=2029 ssh "${server}" "${target_dbscripts}/db-update" || die msg "Removing %s from %s" "${pkgbase}" "${source_repo}" -for _arch in ${arch[@]}; do +for _arch in "${arch[@]}"; do + # shellcheck disable=2029 ssh "${server}" "${source_dbscripts}/db-remove ${source_repo} ${_arch} ${pkgbase}" done svn -q checkout -N "${source_svn}" source_checkout diff --git a/find-libdeps.in b/find-libdeps.in index 794a2cd..cb68237 100644 --- a/find-libdeps.in +++ b/find-libdeps.in @@ -28,10 +28,10 @@ usage() { echo print ' <soname>=<soversion>-<soarch>' echo - prose 'Where <soversion> is the shared library version, or + prose "Where <soversion> is the shared library version, or <soname> repeated if there is no version attached; and - <soarch> is the architecture of the library (either `32` - or `64`, based on the ELF Class).' + <soarch> is the architecture of the library (either \`32\` + or \`64\`, based on the ELF Class)." echo print "Options:" flag "--ignore-internal" "Ignore internal libraries; libraries @@ -48,7 +48,7 @@ if [[ $1 = '-h' ]]; then fi if [[ -d $1 ]]; then - pushd $1 >/dev/null + pushd "$1" >/dev/null else setup_workdir @@ -65,10 +65,10 @@ process_sofile() { soname="${sofile%.so?(+(.+([0-9])))}".so # extract the major version: 1 soversion="${sofile##*\.so\.}" - if [[ "$soversion" = "$sofile" ]] && (($IGNORE_INTERNAL)); then + if [[ "$soversion" = "$sofile" ]] && ((IGNORE_INTERNAL)); then continue fi - if ! in_array "${soname}=${soversion}-${soarch}" ${soobjects[@]}; then + if ! in_array "${soname}=${soversion}-${soarch}" "${soobjects[@]}"; then # libfoo.so=1-64 echo "${soname}=${soversion}-${soarch}" soobjects+=("${soname}=${soversion}-${soarch}") @@ -80,7 +80,7 @@ case $script_mode in provides) find_args=(-name '*.so*');; esac -find . -type f "${find_args[@]}" | while read filename; do +find . -type f "${find_args[@]}" | while read -r filename; do if [[ $script_mode = "provides" ]]; then # ignore if we don't have a shared object if ! LC_ALL=C readelf -h "$filename" 2>/dev/null | grep -q '.*Type:.*DYN (Shared object file).*'; then diff --git a/finddeps.in b/finddeps.in index cc1ffab..5f89b55 100644 --- a/finddeps.in +++ b/finddeps.in @@ -23,24 +23,25 @@ if [[ $match = '-h' ]]; then exit 0 fi -find . -type d | while read d; do +find . -type d | while read -r d; do if [[ -f "$d/PKGBUILD" ]]; then - unset pkgname depends makedepends optdepends + pkgname=() depends=() makedepends=() optdepends=() + # shellcheck source=PKGBUILD.proto . "$d/PKGBUILD" for dep in "${depends[@]}"; do # lose the version comparator, if any depname=${dep%%[<>=]*} - [[ $depname = $match ]] && echo "$d (depends)" + [[ $depname = "$match" ]] && echo "$d (depends)" done for dep in "${makedepends[@]}"; do # lose the version comparator, if any depname=${dep%%[<>=]*} - [[ $depname = $match ]] && echo "$d (makedepends)" + [[ $depname = "$match" ]] && echo "$d (makedepends)" done for dep in "${optdepends[@]/:*}"; do # lose the version comaparator, if any depname=${dep%%[<>=]*} - [[ $depname = $match ]] && echo "$d (optdepends)" + [[ $depname = "$match" ]] && echo "$d (optdepends)" done fi done @@ -10,8 +10,8 @@ usage() { print "Usage: %s [-h]" "${0##*/}" print "Find broken library links on your machine." echo - prose 'Scans $PATH and library directories for ELF files with - references to missing shared libraries.' + prose "Scans \$PATH and library directories for ELF files with + references to missing shared libraries." } if [[ $1 = '-h' ]]; then @@ -36,7 +36,7 @@ for tree in $PATH $libdirs $extras; do msg2 "DIR %s" "$tree" # Get list of files in tree. - files=$(find $tree -type f ! -name '*.a' ! -name '*.la' ! -name '*.py*' ! -name '*.txt' ! -name '*.h' ! -name '*.ttf' ! \ + files=$(find "$tree" -type f ! -name '*.a' ! -name '*.la' ! -name '*.py*' ! -name '*.txt' ! -name '*.h' ! -name '*.ttf' ! \ -name '*.rb' ! -name '*.ko' ! -name '*.pc' ! -name '*.enc' ! -name '*.cf' ! -name '*.def' ! -name '*.rules' ! -name \ '*.cmi' ! -name '*.mli' ! -name '*.ml' ! -name '*.cma' ! -name '*.cmx' ! -name '*.cmxa' ! -name '*.pod' ! -name '*.pm' \ ! -name '*.pl' ! -name '*.al' ! -name '*.tcl' ! -name '*.bs' ! -name '*.o' ! -name '*.png' ! -name '*.gif' ! -name '*.cmo' \ @@ -44,22 +44,22 @@ for tree in $PATH $libdirs $extras; do -name '*.mcopclass' ! -name '*.mcoptype') IFS=$ifs for i in $files; do - if (( $(file $i | grep -c 'ELF') != 0 )); then + if (( $(file "$i" | grep -c 'ELF') != 0 )); then # Is an ELF binary. - if (( $(ldd $i 2>/dev/null | grep -c 'not found') != 0 )); then + if (( $(ldd "$i" 2>/dev/null | grep -c 'not found') != 0 )); then # Missing lib. - echo "$i:" >> $TEMPDIR/raw.txt - ldd $i 2>/dev/null | grep 'not found' >> $TEMPDIR/raw.txt + echo "$i:" >> "$TEMPDIR/raw.txt" + ldd "$i" 2>/dev/null | grep 'not found' >> "$TEMPDIR/raw.txt" fi fi done done -grep '^/' $TEMPDIR/raw.txt | sed -e 's/://g' >> $TEMPDIR/affected-files.txt +grep '^/' "$TEMPDIR/raw.txt" | sed -e 's/://g' >> "$TEMPDIR/affected-files.txt" # invoke pacman -for i in $(cat $TEMPDIR/affected-files.txt); do - pacman -Qo $i | awk '{print $4,$5}' >> $TEMPDIR/pacman.txt -done +while read -r i; do + pacman -Qo "$i" | awk '{print $4,$5}' >> "$TEMPDIR/pacman.txt" +done < "$TEMPDIR/affected-files.txt" # clean list -sort -u $TEMPDIR/pacman.txt >> $TEMPDIR/possible-rebuilds.txt +sort -u "$TEMPDIR/pacman.txt" >> "$TEMPDIR/possible-rebuilds.txt" msg "Files saved to %s" "$TEMPDIR" diff --git a/lib/archroot.sh b/lib/archroot.sh new file mode 100644 index 0000000..98fd2cf --- /dev/null +++ b/lib/archroot.sh @@ -0,0 +1,67 @@ +#!/hint/bash +# License: Unspecified +: + +# shellcheck disable=2034 +CHROOT_VERSION='v4' + +## +# usage : check_root +## +orig_argv=("$0" "$@") +check_root() { + (( EUID == 0 )) && return + if type -P sudo >/dev/null; then + exec sudo -- "${orig_argv[@]}" + else + exec su root -c "$(printf ' %q' "${orig_argv[@]}")" + fi +} + +## +# usage : is_btrfs( $path ) +# return : whether $path is on a btrfs +## +is_btrfs() { + [[ -e "$1" && "$(stat -f -c %T "$1")" == btrfs ]] +} + +## +# usage : is_subvolume( $path ) +# return : whether $path is a the root of a btrfs subvolume (including +# the top-level subvolume). +## +is_subvolume() { + [[ -e "$1" && "$(stat -f -c %T "$1")" == btrfs && "$(stat -c %i "$1")" == 256 ]] +} + +## +# usage : is_same_fs( $path_a, $path_b ) +# return : whether $path_a and $path_b are on the same filesystem +## +is_same_fs() { + [[ "$(stat -c %d "$1")" == "$(stat -c %d "$1")" ]] +} + +## +# usage : subvolume_delete_recursive( $path ) +# +# Find all btrfs subvolumes under and including $path and delete them. +## +subvolume_delete_recursive() { + local subvol + + is_subvolume "$1" || return 0 + + while IFS= read -d $'\0' -r subvol; do + if ! subvolume_delete_recursive "$subvol"; then + return 1 + fi + done < <(find "$1" -mindepth 1 -xdev -depth -inum 256 -print0) + if ! btrfs subvolume delete "$1" &>/dev/null; then + error "Unable to delete subvolume %s" "$subvol" + return 1 + fi + + return 0 +} diff --git a/lib/common.sh b/lib/common.sh index 68b001d..118a06c 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -25,7 +25,7 @@ _l() { shopt -s extglob # check if messages are to be printed using color -declare ALL_OFF= BOLD= BLUE= GREEN= RED= YELLOW= +declare ALL_OFF='' BOLD='' BLUE='' GREEN='' RED='' YELLOW='' if [[ -t 2 ]]; then # prefer terminal safe colored and bold text when tput is supported if tput setaf 0 &>/dev/null; then @@ -47,36 +47,43 @@ fi readonly ALL_OFF BOLD BLUE GREEN RED YELLOW plain() { - local mesg="$(_ "$1")"; shift + local mesg; mesg="$(_ "$1")"; shift + # shellcheck disable=2059 printf "${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } msg() { - local mesg="$(_ "$1")"; shift + local mesg; mesg="$(_ "$1")"; shift + # shellcheck disable=2059 printf "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } msg2() { - local mesg="$(_ "$1")"; shift + local mesg; mesg="$(_ "$1")"; shift + # shellcheck disable=2059 printf "${BLUE} ->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } warning() { - local mesg="$(_ "$1")"; shift - printf "${YELLOW}==> $(_l _ "WARNING:")${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 + local mesg; mesg="$(_ "$1")"; shift + # shellcheck disable=2059 + printf "${YELLOW}==> WARNING:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } error() { - local mesg="$(_ "$1")"; shift - printf "${RED}==> $(_l _ "ERROR:")${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 + local mesg; mesg="$(_ "$1")"; shift + # shellcheck disable=2059 + printf "${RED}==> ERROR:${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2 } stat_busy() { - local mesg="$(_ "$1")"; shift + local mesg; mesg="$(_ "$1")"; shift + # shellcheck disable=2059 printf "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}...${ALL_OFF}" "$@" >&2 } stat_done() { + # shellcheck disable=2059 printf "${BOLD}$(_l _ "done")${ALL_OFF}\n" >&2 } @@ -92,7 +99,7 @@ cleanup() { if [[ -n ${WORKDIR:-} ]] && $_setup_workdir; then rm -rf "$WORKDIR" fi - exit ${1:-0} + exit "${1:-0}" } abort() { @@ -125,7 +132,7 @@ in_array() { local needle=$1; shift local item for item in "$@"; do - [[ $item = $needle ]] && return 0 # Found + [[ $item = "$needle" ]] && return 0 # Found done return 1 # Not Found } @@ -136,24 +143,27 @@ in_array() { ## get_full_version() { # set defaults if they weren't specified in buildfile - pkgbase=${pkgbase:-${pkgname[0]}} - epoch=${epoch:-0} + local pkgbase=${pkgbase:-${pkgname[0]}} + local epoch=${epoch:-0} + local pkgver=${pkgver} + local pkgrel=${pkgrel} if [[ -z $1 ]]; then if (( ! epoch )); then - echo $pkgver-$pkgrel + printf '%s\n' "$pkgver-$pkgrel" else - echo $epoch:$pkgver-$pkgrel + printf '%s\n' "$epoch:$pkgver-$pkgrel" fi else + local pkgver_override='' pkgrel_override='' epoch_override='' for i in pkgver pkgrel epoch; do local indirect="${i}_override" - eval $(declare -f package_$1 | sed -n "s/\(^[[:space:]]*$i=\)/${i}_override=/p") + eval "$(declare -f "package_$1" | sed -n "s/\(^[[:space:]]*$i=\)/${i}_override=/p")" [[ -z ${!indirect} ]] && eval ${indirect}=\"${!i}\" done - if (( ! $epoch_override )); then - echo $pkgver_override-$pkgrel_override + if (( ! epoch_override )); then + printf '%s\n' "$pkgver_override-$pkgrel_override" else - echo $epoch_override:$pkgver_override-$pkgrel_override + printf '%s\n' "$epoch_override:$pkgver_override-$pkgrel_override" fi fi } @@ -164,13 +174,13 @@ get_full_version() { lock() { # Only reopen the FD if it wasn't handed to us if ! [[ "/dev/fd/$1" -ef "$2" ]]; then - mkdir -p "${2%/*}" + mkdir -p -- "$(dirname -- "$2")" eval "exec $1>"'"$2"' fi - if ! flock -n $1; then + if ! flock -n "$1"; then stat_busy "${@:3}" - flock $1 + flock "$1" stat_done fi } @@ -181,13 +191,13 @@ lock() { slock() { # Only reopen the FD if it wasn't handed to us if ! [[ "/dev/fd/$1" -ef "$2" ]]; then - mkdir -p "${2%/*}" + mkdir -p -- "$(dirname -- "$2")" eval "exec $1>"'"$2"' fi - if ! flock -sn $1; then + if ! flock -sn "$1"; then stat_busy "${@:3}" - flock -s $1 + flock -s "$1" stat_done fi } @@ -197,6 +207,8 @@ slock() { ## lock_close() { local fd=$1 + # https://github.com/koalaman/shellcheck/issues/862 + # shellcheck disable=2034 exec {fd}>&- } @@ -204,8 +216,6 @@ lock_close() { # usage: pkgver_equal( $pkgver1, $pkgver2 ) ## pkgver_equal() { - local left right - if [[ $1 = *-* && $2 = *-* ]]; then # if both versions have a pkgrel, then they must be an exact match [[ $1 = "$2" ]] @@ -224,7 +234,7 @@ pkgver_equal() { find_cached_package() { local searchdirs=("$PWD" "$PKGDEST") results=() local targetname=$1 targetver=$2 targetarch=$3 - local dir pkg pkgbasename pkgparts name ver rel arch size r results + local dir pkg pkgbasename name ver rel arch r results for dir in "${searchdirs[@]}"; do [[ -d $dir ]] || continue @@ -262,7 +272,7 @@ find_cached_package() { return 1 ;; 1) - printf '%s\n' "$results" + printf '%s\n' "${results[0]}" return 0 ;; *) @@ -273,13 +283,58 @@ find_cached_package() { } ## -# usage : check_root ("$0" "$@") +# usage: find_cached_srcpackage( $pkgname, $pkgver, $arch ) +# +# $pkgver can be supplied with or without a pkgrel appended. +# If not supplied, any pkgrel will be matched. ## -check_root() { - (( EUID == 0 )) && return - if type -P sudo >/dev/null; then - exec sudo -- "$@" - else - exec su root -c "$(printf ' %q' "$@")" - fi +find_cached_srcpackage() { + local searchdirs=("$PWD" "$SRCPKGDEST") results=() + local targetname=$1 targetver=$2 targetarch=$3 + local dir pkg pkgbasename name ver rel arch r results + + for dir in "${searchdirs[@]}"; do + [[ -d $dir ]] || continue + + for pkg in "$dir"/*.src.tar?(.?z); do + [[ -f $pkg ]] || continue + + # avoid adding duplicates of the same inode + for r in "${results[@]}"; do + [[ $r -ef $pkg ]] && continue 2 + done + + # split apart package filename into parts + pkgbasename=${pkg##*/} + pkgbasename=${pkgbasename%.src.tar?(.?z)} + + arch=${pkgbasename##*-} + pkgbasename=${pkgbasename%-"$arch"} + + rel=${pkgbasename##*-} + pkgbasename=${pkgbasename%-"$rel"} + + ver=${pkgbasename##*-} + name=${pkgbasename%-"$ver"} + + if [[ $targetname = "$name" && $targetarch = "$arch" ]] && + pkgver_equal "$targetver" "$ver-$rel"; then + results+=("$pkg") + fi + done + done + + case ${#results[*]} in + 0) + return 1 + ;; + 1) + printf '%s\n' "${results[0]}" + return 0 + ;; + *) + _l error 'Multiple packages found:' + printf '\t%s\n' "${results[@]}" >&2 + return 1 + esac } diff --git a/lib/valid-tags.sh b/lib/valid-tags.sh index 0543ab2..2916dc7 100644 --- a/lib/valid-tags.sh +++ b/lib/valid-tags.sh @@ -1,11 +1,15 @@ +#!/hint/bash # License: Unspecified +: +# shellcheck disable=2034 _arch=( i686 x86_64 any ) +# shellcheck disable=2034 _tags=( core-i686 core-x86_64 core-any extra-i686 extra-x86_64 extra-any diff --git a/makechrootpkg.in b/makechrootpkg.in index c6ef240..7589737 100644 --- a/makechrootpkg.in +++ b/makechrootpkg.in @@ -11,6 +11,7 @@ # GNU General Public License for more details. m4_include(lib/common.sh) +m4_include(lib/archroot.sh) shopt -s nullglob @@ -20,13 +21,13 @@ init_variables() { repack=false update_first=false clean_first=false - install_pkg= run_namcap=false temp_chroot=false chrootdir= passeddir= - declare -a install_pkgs - declare -i ret=0 + makepkg_user= + declare -ga install_pkgs + declare -gi ret=0 bindmounts_ro=() bindmounts_rw=() @@ -51,6 +52,10 @@ usage() { echo 'command:' echo ' mkarchroot <chrootdir>/root base-devel' echo '' + echo 'This script reads {SRC,SRCPKG,PKG,LOG}DEST, MAKEFLAGS and PACKAGER' + echo 'from makepkg.conf(5), if those variables are not part of the' + echo 'environment.' + echo '' echo "Default makepkg args: ${default_makepkg_args[*]}" echo '' echo 'Flags:' @@ -68,6 +73,7 @@ usage() { echo " Default: $copy" echo '-n Run namcap on the package' echo '-T Build in a temporary directory' + echo '-U Run makepkg as a specified user' exit 1 } @@ -86,136 +92,43 @@ load_vars() { [[ -f $makepkg_conf ]] || return 1 for var in {SRC,SRCPKG,PKG,LOG}DEST MAKEFLAGS PACKAGER; do - [[ -z ${!var:-} ]] && eval $(grep "^${var}=" "$makepkg_conf") + [[ -z ${!var:-} ]] && eval "$(grep "^${var}=" "$makepkg_conf")" done return 0 } -# Usage: btrfs_subvolume_id $SUBVOLUME -btrfs_subvolume_id() ( - set -o pipefail - LC_ALL=C btrfs subvolume show "$1" | sed -n 's/^\tSubvolume ID:\s*//p' -) - -# Usage: btrfs_subvolume_list_all $FILEPATH -# -# Given $FILEPATH somewhere on a mounted btrfs filesystem, print the -# ID and full path of every subvolume on the filesystem, one per line -# in the format "$ID $PATH", where $PATH is relative to the top-level -# subvolume (which might not be what is mounted). -# -# BUG: Due to limitations in the `btrfs` tool, this will not correctly -# list subvolumes whose path contains a space. -btrfs_subvolume_list_all() ( - set -o pipefail - - local mountpoint all - mountpoint="$(df --output=target "$1" | sed 1d)" || return - # The output of `btrfs subvolume list -a` is a space-separated - # sequence of "key value key value...". Unfortunately both - # keys and values can contain space, and there's no escaping - # or indication of when this happens. So we assume - # 1. ID is the first column - # 2. That no key or value will contain " path" - # 3. That the "path" value does not contain a space. - all="$(LC_ALL=C btrfs subvolume list -a "$mountpoint" | sed -r 's|^ID ([0-9]+) .* path (<FS_TREE>/)?(\S*).*|\1 \3|')" || return - - # Sanity check the output - local id path - while read -r id path; do - # ID should be numeric - [[ "$id" =~ ^-?[0-9]+$ ]] || return - # While a path could countain a space, the above code - # doesn't support it; if there is space, then it means - # we got a line not matching the expected format. - [[ "$path" != *' '* ]] || return - done <<<"$all" - - printf '%s\n' "$all" -) - -# Usage: btrfs_subvolume_list $SUBVOLUME -# -# Assuming that $SUBVOLUME is a btrfs subvolume, list all child -# subvolumes; from most deeply nested to most shallowly nested. -# -# This is intended to be a sane version of `btrfs subvolume list`. -btrfs_subvolume_list() { - local subvolume=$1 - - local id all path subpath - id="$(btrfs_subvolume_id "$subvolume")" || return - all="$(btrfs_subvolume_list_all "$subvolume")" || return - path=$(awk -v id="$id" '$1 == id { sub($1 FS, ""); print }' <<<"$all") - while read -r id subpath; do - if [[ "$subpath" = "$path"/* ]]; then - printf '%s\n' "${subpath#"${path}/"}" - fi - done <<<"$all" | LC_ALL=C sort --reverse -} - -# Usage: btrfs_subvolume_delete $SUBVOLUME -# -# Assuming that $SUBVOLUME is a btrfs subvolume, delete it and all -# subvolumes below it. -# -# This is intended to be a recursive version of -# `btrfs subvolume delete`. -btrfs_subvolume_delete() { - local dir="$1" - - # We store the result as a variable because we want to see if - # btrfs_subvolume_list fails or succeeds before we start - # deleting things. (Then we have to work around the subshell - # trimming the trailing newlines.) - local subvolumes - subvolumes="$(btrfs_subvolume_list "$dir")" || return - [[ -z "$subvolumes" ]] || subvolumes+=$'\n' - - local subvolume - while read -r subvolume; do - btrfs subvolume delete "$dir/$subvolume" || return - done < <(printf '%s' "$subvolumes") - - btrfs subvolume delete "$dir" -} - -# Usage: sync_chroot $CHROOTDIR/$CHROOT <$CHROOTCOPY|$copydir> +# Usage: sync_chroot $rootdir $copydir [$copy] sync_chroot() { - local chrootdir=$1 - local copy=$2 - local copydir='' - if [[ ${copy:0:1} = / ]]; then - copydir=$copy - else - copydir="$chrootdir/$copy" - fi + local rootdir=$1 + local copydir=$2 + local copy=${3:-$2} - if [[ "$chrootdir/root" -ef "$copydir" ]]; then + if [[ "$rootdir" -ef "$copydir" ]]; then error 'Cannot sync copy with itself: %s' "$copydir" return 1 fi - # Detect chrootdir filesystem type - local chroottype=$(stat -f -c %T "$chrootdir") - # Get a read lock on the root chroot to make # sure we don't clone a half-updated chroot - slock 8 "$chrootdir/root.lock" \ - "Locking clean chroot [%s]" "$chrootdir/root" + slock 8 "$rootdir.lock" \ + "Locking clean chroot [%s]" "$rootdir" - stat_busy "Synchronizing chroot copy [%s] -> [%s]" "$chrootdir/root" "$copydir" - if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then - if [[ -d $copydir ]]; then - btrfs_subvolume_delete "$copydir" >/dev/null || + stat_busy "Synchronizing chroot copy [%s] -> [%s]" "$rootdir" "$copy" + if is_subvolume "$rootdir" && is_same_fs "$rootdir" "$(dirname -- "$copydir")" && ! mountpoint -q "$copydir"; then + if is_subvolume "$copydir"; then + subvolume_delete_recursive "$copydir" || die "Unable to delete subvolume %s" "$copydir" + else + # avoid change of filesystem in case of an umount failure + rm --recursive --force --one-file-system "$copydir" || + die "Unable to delete %s" "$copydir" fi - btrfs subvolume snapshot "$chrootdir/root" "$copydir" >/dev/null || + btrfs subvolume snapshot "$rootdir" "$copydir" >/dev/null || die "Unable to create subvolume %s" "$copydir" else mkdir -p "$copydir" - rsync -a --delete -q -W -x "$chrootdir/root/" "$copydir" + rsync -a --delete -q -W -x "$rootdir/" "$copydir" fi stat_done @@ -226,15 +139,14 @@ sync_chroot() { touch "$copydir" } -# Usage: delete_chroot $copydir +# Usage: delete_chroot $copydir [$copy] delete_chroot() { local copydir=$1 - # Detect chrootdir filesystem type - local chroottype=$(stat -f -c %T "$copydir") + local copy=${1:-$2} - stat_busy "Removing chroot copy [%s]" "$copydir" - if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then - btrfs_subvolume_delete "$copydir" >/dev/null || + stat_busy "Removing chroot copy [%s]" "$copy" + if is_subvolume "$copydir" && ! mountpoint -q "$copydir"; then + subvolume_delete_recursive "$copydir" || die "Unable to delete subvolume %s" "$copydir" else # avoid change of filesystem in case of an umount failure @@ -278,8 +190,9 @@ prepare_chroot() { $repack || rm -rf "$copydir/build" - local builduser_uid="${SUDO_UID:-$UID}" - local builduser_gid="$(id -g "$builduser_uid")" + local builduser_uid builduser_gid + builduser_uid="${SUDO_UID:-$UID}" + builduser_gid="$(id -g "$builduser_uid")" local install="install -o $builduser_uid -g $builduser_gid" local x @@ -287,8 +200,8 @@ prepare_chroot() { # which we might not be able to load (i.e. when building i686 packages on # an x86_64 host). sed -e '/^builduser:/d' -i "$copydir"/etc/{passwd,group} - printf >>"$copydir/etc/group" 'builduser:x:%d:\n' $builduser_gid - printf >>"$copydir/etc/passwd" 'builduser:x:%d:%d:builduser:/build:/bin/bash\n' $builduser_uid $builduser_gid + printf >>"$copydir/etc/group" 'builduser:x:%d:\n' "$builduser_gid" + printf >>"$copydir/etc/passwd" 'builduser:x:%d:%d:builduser:/build:/bin/bash\n' "$builduser_uid" "$builduser_gid" $install -d "$copydir"/{build,build/.gnupg,startdir,{pkg,srcpkg,src,log}dest} @@ -306,13 +219,13 @@ prepare_chroot() { done cat > "$copydir/etc/sudoers.d/builduser-pacman" <<EOF -Defaults env_keep += "HOME" builduser ALL = NOPASSWD: /usr/bin/pacman EOF chmod 440 "$copydir/etc/sudoers.d/builduser-pacman" if ! grep -q '^\[repo\]' "$copydir/etc/pacman.conf"; then - local line=$(grep -n '^\[' "$copydir/etc/pacman.conf" |grep -Fv ':[options]'|sed 's/:.*//;1q') + local line + line=$(grep -n '^\[' "$copydir/etc/pacman.conf" |grep -Fv ':[options]'|sed 's/:.*//;1q') local ins='[repo] SigLevel = Optional TrustAll Server = file:///repo @@ -347,12 +260,36 @@ Server = file:///repo # These functions aren't run in makechrootpkg, # so no global variables _chrootprepare() { + # shellcheck source=/dev/null . /etc/profile + # Beware, there are some stupid arbitrary rules on how you can + # use "$" in arguments to commands with "sudo -i". ${foo} or + # ${1} is OK, but $foo or $1 isn't. + # https://bugzilla.sudo.ws/show_bug.cgi?id=765 sudo -iu builduser bash -c 'cd /startdir; makepkg "$@" --nobuild' -bash "$@" } _chrootbuild() { + # shellcheck source=/dev/null . /etc/profile + local srcext + srcext="$( + # shellcheck source=makepkg-x86_64.conf + . /etc/makepkg.conf || exit + # shellcheck source=PKGBUILD.proto + . /startdir/PKGBUILD || exit + if [ "$arch" = any ]; then + pkgarch=any + else + pkgarch=$CARCH + fi + printf '%s\n' "-$pkgarch$SRCEXT" + )" || return + # Beware, there are some stupid arbitrary rules on how you can + # use "$" in arguments to commands with "sudo -i". ${foo} or + # ${1} is OK, but $foo or $1 isn't. + # https://bugzilla.sudo.ws/show_bug.cgi?id=765 + sudo -iu builduser bash -c 'cd /startdir; SRCEXT="${1}" makepkg "${@:2}" --allsource' -bash "$srcext" "$@" || return sudo -iu builduser bash -c 'cd /startdir; makepkg "$@" --noextract --noprepare' -bash "$@" } @@ -364,27 +301,27 @@ _chrootnamcap() { done } -# Usage: download_sources $copydir $src_owner +# Usage: download_sources $copydir $makepkg_user # Globals: # - SRCDEST # - USER download_sources() { local copydir=$1 - local src_owner=$2 + local makepkg_user=$2 - local builddir="$(mktemp -d)" + local builddir + builddir="$(mktemp -d)" chmod 1777 "$builddir" # Ensure sources are downloaded - if [[ $USER != $src_owner ]]; then - sudo -u $src_owner env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \ - makepkg --config="$copydir/etc/makepkg.conf" --verifysource -o + if [[ "$(id -u "$makepkg_user")" != 0 ]]; then + sudo -u "$makepkg_user" env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \ + makepkg --config="$copydir/etc/makepkg.conf" --verifysource -o || + die "Could not download sources." else - ( export SRCDEST BUILDDIR="$builddir" - makepkg --asroot --config="$copydir/etc/makepkg.conf" --verifysource -o - ) + error "Running makepkg as root is not allowed." + exit 1 fi - (( $? != 0 )) && die "Could not download sources." # Clean up garbage from verifysource rm -rf "$builddir" @@ -402,8 +339,10 @@ move_products() { for pkgfile in "$copydir"/pkgdest/*; do chown "$src_owner" "$pkgfile" mv "$pkgfile" "$PKGDEST" - if [[ $PKGDEST != $PWD ]]; then - ln -sf "$PKGDEST/${pkgfile##*/}" . + + # Fix broken symlink because of temporary chroot PKGDEST /pkgdest + if [[ "$PWD" != "$PKGDEST" && -L "$PWD/${pkgfile##*/}" ]]; then + ln -sf "$PKGDEST/${pkgfile##*/}" fi done @@ -424,26 +363,27 @@ move_products() { main() { init_variables - orig_argv=("$@") - - while getopts 'hcur:I:l:nTD:d:' arg; do + while getopts 'hcur:I:l:nTD:d:U:' arg; do case "$arg" in c) clean_first=true ;; - D) bindmounts_ro+=(--bind-ro="$OPTARG") ;; - d) bindmounts_rw+=(--bind="$OPTARG") ;; + D) bindmounts_ro+=("--bind-ro=$OPTARG") ;; + d) bindmounts_rw+=("--bind=$OPTARG") ;; u) update_first=true ;; r) passeddir="$OPTARG" ;; I) install_pkgs+=("$OPTARG") ;; l) copy="$OPTARG" ;; n) run_namcap=true; makepkg_args+=(-i) ;; T) temp_chroot=true; copy+="-$$" ;; + U) makepkg_user="$OPTARG" ;; h|*) usage ;; esac done [[ ! -f PKGBUILD && -z "${install_pkgs[*]}" ]] && die 'This must be run in a directory containing a PKGBUILD.' + [[ -n $makepkg_user && -z $(id -u "$makepkg_user") ]] && die 'Invalid makepkg user.' + makepkg_user=${makepkg_user:-${SUDO_USER:-$USER}} - check_root "$0" "${orig_argv[@]}" + check_root # Canonicalize chrootdir, getting rid of trailing / chrootdir=$(readlink -e "$passeddir") @@ -490,7 +430,7 @@ main() { lock 9 "$copydir.lock" "Locking chroot copy [%s]" "$copy" if [[ ! -d $copydir ]] || $clean_first; then - sync_chroot "$chrootdir" "$copy" + sync_chroot "$chrootdir/root" "$copydir" "$copy" fi $update_first && arch-nspawn "$copydir" \ @@ -504,7 +444,7 @@ main() { [[ -f PKGBUILD ]] || return $ret fi - download_sources "$copydir" "$src_owner" + download_sources "$copydir" "$makepkg_user" prepare_chroot "$copydir" "$USER_HOME" "$repack" @@ -524,7 +464,7 @@ main() { (( ret += 1 )) fi - $temp_chroot && delete_chroot "$copydir" + $temp_chroot && delete_chroot "$copydir" "$copy" if (( ret != 0 )); then if $temp_chroot; then diff --git a/makepkg-i686.conf b/makepkg-i686.conf index c565795..4cbc28b 100644 --- a/makepkg-i686.conf +++ b/makepkg-i686.conf @@ -1,3 +1,6 @@ +#!/hint/bash +# shellcheck disable=2034 + # # /etc/makepkg.conf # diff --git a/makepkg-x86_64.conf b/makepkg-x86_64.conf index 058da9b..79858a7 100644 --- a/makepkg-x86_64.conf +++ b/makepkg-x86_64.conf @@ -1,3 +1,6 @@ +#!/hint/bash +# shellcheck disable=2034 + # # /etc/makepkg.conf # diff --git a/mkarchroot.in b/mkarchroot.in index f86ae35..5165960 100644 --- a/mkarchroot.in +++ b/mkarchroot.in @@ -11,8 +11,7 @@ # GNU General Public License for more details. m4_include(lib/common.sh) - -CHROOT_VERSION='v3' +m4_include(lib/archroot.sh) working_dir='' @@ -30,8 +29,6 @@ usage() { exit 1 } -orig_argv=("$@") - while getopts 'hC:M:c:f:s' arg; do case "$arg" in C) pac_conf="$OPTARG" ;; @@ -43,19 +40,19 @@ while getopts 'hC:M:c:f:s' arg; do *) error "invalid argument '%s'" "$arg"; usage ;; esac done -shift $(($OPTIND - 1)) +shift $((OPTIND - 1)) (( $# < 2 )) && die 'You must specify a directory and one or more packages.' -check_root "$0" "${orig_argv[@]}" +check_root -working_dir="$(readlink -f $1)" +working_dir="$(readlink -f "$1")" shift 1 [[ -z $working_dir ]] && die 'Please specify a working directory.' if [[ -z $cache_dir ]]; then - cache_dirs=($(pacman -v $cache_conf 2>&1 | grep '^Cache Dirs:' | sed 's/Cache Dirs:\s*//g')) + cache_dirs=($(pacman -v "$cache_conf" 2>&1 | grep '^Cache Dirs:' | sed 's/Cache Dirs:\s*//g')) else cache_dirs=(${cache_dir}) fi @@ -68,7 +65,7 @@ mkdir -p "$working_dir" lock 9 "${working_dir}.lock" "Locking chroot" -if [[ $(stat -f -c %T "$working_dir") == btrfs ]]; then +if is_btrfs "$working_dir"; then rmdir "$working_dir" if ! btrfs subvolume create "$working_dir"; then die "Couldn't create subvolume for '%s'" "$working_dir" @@ -90,7 +87,7 @@ pacstrap -GMcd ${pac_conf:+-C "$pac_conf"} "$working_dir" \ "${cache_dirs[@]/#/--cachedir=}" "$@" || die 'Failed to install all packages' printf '%s.UTF-8 UTF-8\n' en_US de_DE > "$working_dir/etc/locale.gen" -echo 'LANG=C' > "$working_dir/etc/locale.conf" +echo 'LANG=en_US.UTF-8' > "$working_dir/etc/locale.conf" echo "$CHROOT_VERSION" > "$working_dir/.arch-chroot" systemd-machine-id-setup --root="$working_dir" diff --git a/rebuildpkgs.in b/rebuildpkgs.in index 215439f..4f4f98b 100644 --- a/rebuildpkgs.in +++ b/rebuildpkgs.in @@ -14,18 +14,28 @@ m4_include(lib/common.sh) if (( $# < 1 )); then - echo "Usage: $(basename $0) <chrootdir> <packages to rebuild>" - echo " example: $(basename $0) ~/chroot readline bash foo bar baz" + printf 'Usage: %s <chrootdir> <packages to rebuild>\n' "$(basename "$0")" + printf ' example: %s ~/chroot readline bash foo bar baz\n' "$(basename "$0")" exit 1 fi # Source makepkg.conf; fail if it is not found if [[ -r '/etc/makepkg.conf' ]]; then + # shellcheck source=makepkg-x86_64.conf source '/etc/makepkg.conf' else die '/etc/makepkg.conf not found!' fi +# Source user-specific makepkg.conf overrides +if [[ -r "${XDG_CONFIG_HOME:-$HOME/.config}/pacman/makepkg.conf" ]]; then + # shellcheck source=/dev/null + source "${XDG_CONFIG_HOME:-$HOME/.config}/pacman/makepkg.conf" +elif [[ -r "$HOME/.makepkg.conf" ]]; then + # shellcheck source=/dev/null + source "$HOME/.makepkg.conf" +fi + bump_pkgrel() { # Get the current pkgrel from SVN and update the working copy with it # This prevents us from incrementing out of control :) @@ -33,9 +43,9 @@ bump_pkgrel() { oldrel=$(grep 'pkgrel=' $pbuild | cut -d= -f2) #remove decimals - rel=$(echo $oldrel | cut -d. -f1) + rel=${oldrel%%.*} - newrel=$(($rel + 1)) + newrel=$((rel + 1)) sed -i "s/pkgrel=$oldrel/pkgrel=$newrel/" PKGBUILD } @@ -43,11 +53,12 @@ bump_pkgrel() { pkg_from_pkgbuild() { # we want the sourcing to be done in a subshell so we don't pollute our current namespace export CARCH PKGEXT + # shellcheck source=PKGBUILD.proto (source PKGBUILD; echo "$pkgname-$pkgver-$pkgrel-$CARCH$PKGEXT") } chrootdir="$1"; shift -pkgs="$@" +pkgs=("$@") SVNPATH='svn+ssh://repos.archlinux.org/srv/repos/svn-packages/svn' @@ -60,7 +71,7 @@ cd "$REBUILD_ROOT" /usr/bin/svn co -N $SVNPATH FAILED="" -for pkg in $pkgs; do +for pkg in "${pkgs[@]}"; do cd "$REBUILD_ROOT/svn-packages" msg2 "Building '%s'" "$pkg" |