Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/aur/drop-from-repo.sh13
-rw-r--r--src/lib/build/build.sh13
-rw-r--r--src/lib/common.sh30
-rw-r--r--src/lib/db/remove.sh94
-rw-r--r--src/lib/release.sh2
-rw-r--r--src/lib/repo.sh10
-rw-r--r--src/lib/repo/clean.sh114
-rw-r--r--src/lib/repo/configure.sh8
-rw-r--r--src/lib/repo/switch.sh13
-rw-r--r--src/lib/util/makepkg.sh20
-rw-r--r--src/lib/util/pacman.sh36
-rw-r--r--src/lib/util/pkgbuild.sh50
-rw-r--r--src/lib/util/term.sh16
-rw-r--r--src/lib/valid-tags.sh8
-rw-r--r--src/lib/version.sh10
-rw-r--r--src/lib/version/check.sh23
-rw-r--r--src/lib/version/setup.sh528
-rw-r--r--src/lib/version/upgrade.sh66
18 files changed, 1007 insertions, 47 deletions
diff --git a/src/lib/aur/drop-from-repo.sh b/src/lib/aur/drop-from-repo.sh
index 6ebe12a..0e9cab4 100644
--- a/src/lib/aur/drop-from-repo.sh
+++ b/src/lib/aur/drop-from-repo.sh
@@ -92,14 +92,19 @@ pkgctl_aur_drop_from_repo() {
fi
for path in "${paths[@]}"; do
- if ! realpath=$(realpath -e "${path}"); then
+ # resolve symlink for basename
+ if ! realpath=$(realpath --canonicalize-existing -- "${path}"); then
die "No such directory: ${path}"
fi
+ # skip paths that are not directories
+ if [[ ! -d "${realpath}" ]]; then
+ continue
+ fi
pkgbase=$(basename "${realpath}")
pkgbase=${pkgbase%.git}
- if [[ ! -d "${path}/.git" ]]; then
+ if [[ ! -d "${realpath}/.git" ]]; then
die "Not a Git repository: ${path}"
fi
@@ -136,9 +141,9 @@ pkgctl_aur_drop_from_repo() {
if (( FORCE )); then
AUR_OVERWRITE=1 \
GIT_SSH_COMMAND="ssh -o SendEnv=AUR_OVERWRITE" \
- git push --force origin master
+ git push --force --no-follow-tags origin master
else
- git push origin master
+ git push --no-follow-tags origin master
fi
# update the local default branch in case this clone is used in the future
diff --git a/src/lib/build/build.sh b/src/lib/build/build.sh
index 171bb9a..9f724dd 100644
--- a/src/lib/build/build.sh
+++ b/src/lib/build/build.sh
@@ -80,7 +80,7 @@ pkgctl_build_usage() {
EXAMPLES
$ ${COMMAND}
$ ${COMMAND} --rebuild --staging --message 'libyay 0.42 rebuild' libfoo libbar
- $ ${COMMAND} --pkgver 1.42 --release --db-update
+ $ ${COMMAND} --pkgver=1.42 --release --db-update
_EOF_
}
@@ -312,13 +312,17 @@ pkgctl_build() {
# Update pacman cache for auto-detection
if [[ -z ${REPO} ]]; then
- update_pacman_repo_cache
+ update_pacman_repo_cache multilib
# Check valid repos if not resolved dynamically
elif ! in_array "${REPO}" "${DEVTOOLS_VALID_REPOS[@]}"; then
die "Invalid repository target: %s" "${REPO}"
fi
for path in "${paths[@]}"; do
+ # skip paths that are not directories
+ if [[ ! -d "${path}" ]]; then
+ continue
+ fi
pushd "${path}" >/dev/null
if [[ ! -f PKGBUILD ]]; then
@@ -433,10 +437,11 @@ pkgctl_build() {
stat_done
fi
-
# update checksums if any sources are declared
if (( UPDATE_CHECKSUMS )) && (( ${#source[@]} >= 1 )); then
- updpkgsums
+ if ! result=$(pkgbuild_update_checksums /dev/stderr); then
+ die "${result}"
+ fi
fi
# re-source the PKGBUILD if it changed
diff --git a/src/lib/common.sh b/src/lib/common.sh
index ff767c6..5416eaf 100644
--- a/src/lib/common.sh
+++ b/src/lib/common.sh
@@ -15,6 +15,9 @@ $DEVTOOLS_INCLUDE_COMMON_SH
# Avoid any encoding problems
export LANG=C.UTF-8
+# Avoid systemd trying to color the terminal on systemd-nspawn
+export SYSTEMD_TINT_BACKGROUND=no
+
# Set buildtool properties
export BUILDTOOL=devtools
export BUILDTOOLVER=@buildtoolver@
@@ -31,6 +34,17 @@ export PACKAGING_REPO_RELEASE_HOST=repos.archlinux.org
export PKGBASE_MAINTAINER_URL=https://archlinux.org/packages/pkgbase-maintainer
export AUR_URL_SSH=aur@aur.archlinux.org
+export RSYNC_OPTS=(
+ --rsh=ssh
+ --checksum
+ --copy-links
+ --human-readable
+ --progress
+ --partial
+ --partial-dir=.partial
+ --delay-updates
+)
+
# ensure TERM is set with a fallback to dumb
export TERM=${TERM:-dumb}
@@ -106,6 +120,8 @@ print_workdir_error() {
}
_setup_workdir=false
+# Ensure that there is no outside value for WORKDIR leaking in
+unset WORKDIR
setup_workdir() {
[[ -z ${WORKDIR:-} ]] && WORKDIR=$(mktemp -d --tmpdir "${0##*/}.XXXXXXXXXX")
_setup_workdir=true
@@ -342,3 +358,17 @@ is_debug_package() {
pkgdesc="$(getpkgdesc "${pkgfile}")"
[[ ${pkgdesc} == "Detached debugging symbols for "* && ${pkgbase}-debug = "${pkgname}" ]]
}
+
+# Proxy function to check if a file exists. Using [[ -f ... ]] directly is not
+# always wanted because we might want to expand bash globs first. This way we
+# can pass unquoted globs to is_globfile() and have them expanded as function
+# arguments before being checked.
+is_globfile() {
+ [[ -f $1 ]]
+}
+
+join_by() {
+ local IFS="$1"
+ shift
+ echo "$*"
+}
diff --git a/src/lib/db/remove.sh b/src/lib/db/remove.sh
index ba21c83..cddcc1d 100644
--- a/src/lib/db/remove.sh
+++ b/src/lib/db/remove.sh
@@ -8,6 +8,12 @@ DEVTOOLS_INCLUDE_DB_REMOVE_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
+# shellcheck source=src/lib/util/pacman.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/pacman.sh
+# shellcheck source=src/lib/util/term.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/term.sh
+# shellcheck source=src/lib/valid-repos.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-repos.sh
set -e
@@ -17,10 +23,19 @@ pkgctl_db_remove_usage() {
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [REPO] [PKGBASE]...
- Remove packages from binary repositories.
+ Remove packages from pacman repositories. By default passing a pkgbase removes
+ all split packages, debug packages as well as entries from the state repo for
+ all existing architectures.
+
+ Beware when using the --partial option, as it may most likely lead to
+ undesired effects by leaving debug packages behind as well as dangling entries
+ in the state repository.
OPTIONS
- -a, --arch Override the architecture (disables auto-detection)
+ -a, --arch Remove only one specific architecture (disables auto-detection)
+ --partial Remove only partial pkgnames from a split package. This leaves
+ debug packages behind and pkgbase entries in the state repo.
+ --noconfirm Bypass any confirmation messages, should only be used with caution
-h, --help Show this help text
EXAMPLES
@@ -31,8 +46,13 @@ _EOF_
pkgctl_db_remove() {
local REPO=""
- local ARCH=any
local PKGBASES=()
+ local pkgnames=()
+ local partial=0
+ local confirm=1
+ local dbscripts_options=()
+ local lookup_repo=multilib
+ local pkgname
# option checking
while (( $# )); do
@@ -41,11 +61,20 @@ pkgctl_db_remove() {
pkgctl_db_remove_usage
exit 0
;;
+ --partial)
+ partial=1
+ dbscripts_options+=(--partial)
+ shift
+ ;;
-a|--arch)
(( $# <= 1 )) && die "missing argument for %s" "$1"
- ARCH=$2
+ dbscripts_options+=(--arch "$2")
shift 2
;;
+ --noconfirm)
+ confirm=0
+ shift
+ ;;
-*)
die "invalid argument: %s" "$1"
;;
@@ -63,7 +92,62 @@ pkgctl_db_remove() {
REPO=$1
shift
PKGBASES+=("$@")
+ pkgnames=("${PKGBASES[@]}")
+
+ # check if the target repo is valid
+ if ! in_array "${REPO}" "${DEVTOOLS_VALID_REPOS[@]}"; then
+ die "Invalid repository target: %s" "${REPO}"
+ fi
+
+ # update pacman cache to query all pkgnames
+ if (( ! partial )); then
+ case ${REPO} in
+ *-unstable)
+ update_pacman_repo_cache unstable
+ ;;
+ *-staging)
+ update_pacman_repo_cache multilib-staging
+ ;;
+ *-testing)
+ update_pacman_repo_cache multilib-testing
+ ;;
+ *)
+ update_pacman_repo_cache multilib
+ ;;
+ esac
+
+ # fetch the pkgnames of all pkgbase as present in the repo
+ mapfile -t pkgnames < <(get_pkgnames_from_repo_pkgbase "${REPO}" "${PKGBASES[@]}")
+ echo
+
+ if (( ! ${#pkgnames[@]} )); then
+ error "Packages not found in %s" "${REPO}"
+ exit 1
+ fi
+ fi
+
+ # print list of packages
+ printf "%sRemoving packages from %s:%s\n" "${RED}" "${REPO}" "${ALL_OFF}"
+ for pkgname in "${pkgnames[@]}"; do
+ printf "• %s\n" "${pkgname}"
+ done
+
+ # print explenation about partial removal
+ if (( partial )); then
+ echo
+ msg_warn "${YELLOW}Removing only partial pkgnames from a split package.${ALL_OFF}"
+ msg_warn "${YELLOW}This leaves debug packages and pkgbase entries in the state repo!${ALL_OFF}"
+ fi
+
+ # ask for confirmation
+ if (( confirm )); then
+ echo
+ if ! prompt "${GREEN}${BOLD}?${ALL_OFF} Are you sure this is correct?"; then
+ exit 1
+ fi
+ fi
+ echo
# shellcheck disable=SC2029
- ssh "${PACKAGING_REPO_RELEASE_HOST}" db-remove "${REPO}" "${ARCH}" "${PKGBASES[@]}"
+ ssh "${PACKAGING_REPO_RELEASE_HOST}" db-remove "${dbscripts_options[@]}" "${REPO}" "${PKGBASES[@]}"
}
diff --git a/src/lib/release.sh b/src/lib/release.sh
index acb3b54..ba21384 100644
--- a/src/lib/release.sh
+++ b/src/lib/release.sh
@@ -124,7 +124,7 @@ pkgctl_release() {
# Update pacman cache for auto-detection
if [[ -z ${REPO} ]]; then
- update_pacman_repo_cache
+ update_pacman_repo_cache multilib
# Check valid repos if not resolved dynamically
elif ! in_array "${REPO}" "${DEVTOOLS_VALID_REPOS[@]}"; then
die "Invalid repository target: %s" "${REPO}"
diff --git a/src/lib/repo.sh b/src/lib/repo.sh
index 9f545e9..8f8dd0a 100644
--- a/src/lib/repo.sh
+++ b/src/lib/repo.sh
@@ -27,6 +27,7 @@ pkgctl_repo_usage() {
without SSH access using read-only HTTPS.
COMMANDS
+ clean Remove untracked files from the working tree
clone Clone a package repository
configure Configure a clone according to distro specs
create Create a new GitLab package repository
@@ -37,6 +38,7 @@ pkgctl_repo_usage() {
-h, --help Show this help text
EXAMPLES
+ $ ${COMMAND} clean --interactive *
$ ${COMMAND} clone libfoo linux libbar
$ ${COMMAND} clone --maintainer mynickname
$ ${COMMAND} configure *
@@ -59,6 +61,14 @@ pkgctl_repo() {
pkgctl_repo_usage
exit 0
;;
+ clean)
+ _DEVTOOLS_COMMAND+=" $1"
+ shift
+ # shellcheck source=src/lib/repo/clean.sh
+ source "${_DEVTOOLS_LIBRARY_DIR}"/lib/repo/clean.sh
+ pkgctl_repo_clean "$@"
+ exit 0
+ ;;
clone)
_DEVTOOLS_COMMAND+=" $1"
shift
diff --git a/src/lib/repo/clean.sh b/src/lib/repo/clean.sh
new file mode 100644
index 0000000..bb8980e
--- /dev/null
+++ b/src/lib/repo/clean.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[[ -z ${DEVTOOLS_INCLUDE_REPO_CLEAN_SH:-} ]] || return 0
+DEVTOOLS_INCLUDE_REPO_CLEAN_SH=1
+
+_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
+# shellcheck source=src/lib/common.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
+
+source /usr/share/makepkg/util/message.sh
+
+set -eo pipefail
+
+
+pkgctl_repo_clean_usage() {
+ local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
+ cat <<- _EOF_
+ Usage: ${COMMAND} [OPTION] [PATH]...
+
+ Cleans the working tree by recursively removing files that are not under
+ version control, starting from the current directory.
+
+ Files unknown to Git as well as ignored files are removed. This can, for
+ example, be useful to remove all build products.
+
+ OPTIONS
+ -i, --interactive Show what would be done and clean files interactively
+ -n, --dry-run Don't remove anything, just show what would be done
+ -h, --help Show this help text
+
+ EXAMPLES
+ $ ${COMMAND} libfoo linux libbar
+ $ ${COMMAND} --interactive libfoo linux libbar
+ $ ${COMMAND} --dry-run *
+_EOF_
+}
+
+pkgctl_repo_clean() {
+ # options
+ local git_clean_options=()
+ local paths
+
+ local path pkgbase
+
+ while (( $# )); do
+ case $1 in
+ -i|--interactive)
+ git_clean_options+=("$1")
+ shift
+ ;;
+ -n|--dry-run)
+ git_clean_options+=("$1")
+ shift
+ ;;
+ -h|--help)
+ pkgctl_repo_clean_usage
+ exit 0
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ die "invalid argument: %s" "$1"
+ ;;
+ *)
+ paths=("$@")
+ break
+ ;;
+ esac
+ done
+
+ # check if invoked without any path from within a packaging repo
+ if (( ${#paths[@]} == 0 )); then
+ paths=(".")
+ fi
+
+ # print message about the work chunk
+ printf "🗑️ Removing untracked files from %s working trees\n" "${BOLD}${#paths[@]}${ALL_OFF}"
+
+ for path in "${paths[@]}"; do
+ # skip paths that are not directories
+ if [[ ! -d "${path}" ]]; then
+ continue
+ fi
+
+ if [[ ! -f "${path}/PKGBUILD" ]]; then
+ msg_error "Not a package repository: ${path}"
+ continue
+ fi
+
+ if [[ ! -d "${path}/.git" ]]; then
+ msg_error "Not a Git repository: ${path}"
+ continue
+ fi
+
+ pkgbase=$(basename "$(realpath "${path}")")
+ pkgbase=${pkgbase%.git}
+
+ # run dry mode to see if git would clean any files
+ if [[ ! $(git -C "${path}" clean -x -d --dry-run 2>&1) ]]; then
+ continue
+ fi
+
+ # git clean untracked files
+ msg_success "Cleaning ${BOLD}${pkgbase}${ALL_OFF}"
+ if ! git -C "${path}" clean -x -d --force "${git_clean_options[@]}"; then
+ msg_error "Failed to remove untracked files"
+ fi
+ echo
+ done
+}
diff --git a/src/lib/repo/configure.sh b/src/lib/repo/configure.sh
index b3c188c..e9ec5a4 100644
--- a/src/lib/repo/configure.sh
+++ b/src/lib/repo/configure.sh
@@ -207,9 +207,14 @@ pkgctl_repo_configure() {
fi
for path in "${paths[@]}"; do
- if ! realpath=$(realpath -e "${path}"); then
+ # resolve symlink for basename
+ if ! realpath=$(realpath --canonicalize-existing -- "${path}"); then
die "No such directory: ${path}"
fi
+ # skip paths that aren't directories
+ if [[ ! -d "${realpath}" ]]; then
+ continue
+ fi
pkgbase=$(basename "${realpath}")
pkgbase=${pkgbase%.git}
@@ -266,6 +271,7 @@ pkgctl_repo_configure() {
if [[ -n $GPGKEY ]]; then
git config commit.gpgsign true
git config user.signingKey "${GPGKEY}"
+ git config gpg.format openpgp
fi
# set default git exclude
diff --git a/src/lib/repo/switch.sh b/src/lib/repo/switch.sh
index f411ac2..d8ba9df 100644
--- a/src/lib/repo/switch.sh
+++ b/src/lib/repo/switch.sh
@@ -101,16 +101,21 @@ pkgctl_repo_switch() {
fi
for path in "${paths[@]}"; do
- if ! realpath=$(realpath -e -- "${path}"); then
+ # resolve symlink for basename
+ if ! realpath=$(realpath --canonicalize-existing -- "${path}"); then
die "No such directory: ${path}"
fi
- pkgbase=$(basename "${realpath}")
-
- if [[ ! -d "${path}/.git" ]]; then
+ # skip paths that are not directories
+ if [[ ! -d "${realpath}" ]]; then
+ continue
+ fi
+ # skip paths that are not git repositories
+ if [[ ! -d "${realpath}/.git" ]]; then
error "Not a Git repository: ${path}"
continue
fi
+ pkgbase=$(basename "${realpath}")
if ! git -C "${path}" checkout "${GIT_CHECKOUT_OPTIONS[@]}" "${GIT_REF}"; then
die "Failed to switch ${pkgbase} to version ${VERSION}"
fi
diff --git a/src/lib/util/makepkg.sh b/src/lib/util/makepkg.sh
index 22df247..d7ec74c 100644
--- a/src/lib/util/makepkg.sh
+++ b/src/lib/util/makepkg.sh
@@ -22,9 +22,11 @@ makepkg_source_package() {
return
fi
(
+ # shellcheck disable=SC2030 disable=SC2031
export LIBMAKEPKG_LINT_PKGBUILD_SH=1
lint_pkgbuild() { :; }
+ # shellcheck disable=SC2030 disable=SC2031
export LIBMAKEPKG_SRCINFO_SH=1
write_srcinfo() { print_srcinfo; }
@@ -35,3 +37,21 @@ makepkg_source_package() {
source "$(command -v makepkg)"
)
}
+
+makepkg_generate_integrity() {
+ if [[ -z ${DEVTOOLS_GENERATE_INTEGRITY} ]]; then
+ [[ -z ${WORKDIR:-} ]] && setup_workdir
+ export WORKDIR DEVTOOLS_INCLUDE_COMMON_SH
+ bash -$- -c "DEVTOOLS_GENERATE_INTEGRITY=1; source '${BASH_SOURCE[0]}' && ${FUNCNAME[0]}"
+ return
+ fi
+ (
+ # shellcheck disable=SC2030 disable=SC2031
+ export LIBMAKEPKG_LINT_PKGBUILD_SH=1
+ lint_pkgbuild() { :; }
+
+ set +e -- --geninteg
+ # shellcheck source=/usr/bin/makepkg
+ source "$(command -v makepkg)"
+ )
+}
diff --git a/src/lib/util/pacman.sh b/src/lib/util/pacman.sh
index 620e1a8..4637d28 100644
--- a/src/lib/util/pacman.sh
+++ b/src/lib/util/pacman.sh
@@ -18,10 +18,12 @@ readonly _DEVTOOLS_MAKEPKG_CONF_DIR=${_DEVTOOLS_LIBRARY_DIR}/makepkg.conf.d
update_pacman_repo_cache() {
+ local repo=${1:-multilib}
+
mkdir -p "${_DEVTOOLS_PACMAN_CACHE_DIR}"
msg "Updating pacman database cache"
lock 10 "${_DEVTOOLS_PACMAN_CACHE_DIR}.lock" "Locking pacman database cache"
- fakeroot -- pacman --config "${_DEVTOOLS_PACMAN_CONF_DIR}/multilib.conf" \
+ fakeroot -- pacman --config "${_DEVTOOLS_PACMAN_CONF_DIR}/${repo}.conf" \
--dbpath "${_DEVTOOLS_PACMAN_CACHE_DIR}" \
-Sy
lock_close 10
@@ -29,6 +31,7 @@ update_pacman_repo_cache() {
get_pacman_repo_from_pkgbuild() {
local path=${1:-PKGBUILD}
+ local repo=${2:-multilib}
# shellcheck source=contrib/makepkg/PKGBUILD.proto
mapfile -t pkgnames < <(source "${path}"; printf "%s\n" "${pkgname[@]}")
@@ -40,12 +43,12 @@ get_pacman_repo_from_pkgbuild() {
# update the pacman repo cache if it doesn't exist yet
if [[ ! -d "${_DEVTOOLS_PACMAN_CACHE_DIR}" ]]; then
- update_pacman_repo_cache
+ update_pacman_repo_cache "${repo}"
fi
slock 10 "${_DEVTOOLS_PACMAN_CACHE_DIR}.lock" "Locking pacman database cache"
# query repo of passed pkgname, specify --nodeps twice to skip all dependency checks
- mapfile -t repos < <(pacman --config "${_DEVTOOLS_PACMAN_CONF_DIR}/multilib.conf" \
+ mapfile -t repos < <(pacman --config "${_DEVTOOLS_PACMAN_CONF_DIR}/${repo}.conf" \
--dbpath "${_DEVTOOLS_PACMAN_CACHE_DIR}" \
--sync \
--nodeps \
@@ -58,3 +61,30 @@ get_pacman_repo_from_pkgbuild() {
printf "%s" "${repos[0]}"
}
+
+get_pkgnames_from_repo_pkgbase() {
+ local repo=$1
+ shift
+ local pkgbases=("$@")
+
+ # update the pacman repo cache if it doesn't exist yet
+ if [[ ! -d "${_DEVTOOLS_PACMAN_CACHE_DIR}" ]]; then
+ update_pacman_repo_cache universe
+ fi
+
+ slock 10 "${_DEVTOOLS_PACMAN_CACHE_DIR}.lock" "Locking pacman database cache"
+ # query pkgnames of passed pkgbase inside a repo
+ mapfile -t pkgnames < <(expac --config <(sed "s|#DBPath.*|DBPath = $(realpath "${_DEVTOOLS_PACMAN_CACHE_DIR}")|" < "${_DEVTOOLS_PACMAN_CONF_DIR}/universe.conf") \
+ --sync '%r %e %n' 2>/dev/null \
+ | sort | awk -v pkgbase="${pkgbases[*]}" \
+ 'BEGIN { split(pkgbase, array); for (item in array) filter[array[item]]=1 } $1=="'"${repo}"'" && $2 in filter {print $3}'
+ )
+ lock_close 10
+
+ if (( ! ${#pkgnames[@]} )); then
+ return 1
+ fi
+
+ printf "%s\n" "${pkgnames[@]}"
+ return 0
+}
diff --git a/src/lib/util/pkgbuild.sh b/src/lib/util/pkgbuild.sh
index ebf8e5f..245a82f 100644
--- a/src/lib/util/pkgbuild.sh
+++ b/src/lib/util/pkgbuild.sh
@@ -6,10 +6,13 @@
DEVTOOLS_INCLUDE_UTIL_PKGBUILD_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
+# shellcheck source=src/lib/util/makepkg.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/makepkg.sh
source /usr/share/makepkg/util/message.sh
+source /usr/share/makepkg/util/schema.sh
-set -e
+set -eo pipefail
# set the pkgver variable in a PKGBUILD
@@ -41,3 +44,48 @@ pkgbuild_set_pkgrel() {
sed --regexp-extended "s|^(pkgrel=)${pkgrel}$|\1${new_pkgrel}|g" --in-place PKGBUILD
}
+pkgbuild_update_checksums() {
+ local status_file=$1
+ local builddir newbuildfile sumtypes newsums
+
+ [[ -z ${WORKDIR:-} ]] && setup_workdir
+
+ builddir=$(mktemp --tmpdir="${WORKDIR}" --directory update-checksums.XXXXXX)
+ newbuildfile="${builddir}/PKGBUILD"
+
+ # generate new integrity checksums
+ if ! newsums=$(BUILDDIR=${builddir} makepkg_generate_integrity 2>"${status_file}"); then
+ printf 'Failed to generate new checksums'
+ return 1
+ fi
+
+ # early exit if no integrity checksums are needed
+ if [[ -z ${newsums} ]]; then
+ return 0
+ fi
+
+ # replace the integrity sums and write it to a temporary file
+ sumtypes=$(IFS='|'; echo "${known_hash_algos[*]}")
+ if ! awk --assign=sumtypes="${sumtypes}" --assign=newsums="${newsums}" '
+ $0 ~"^[[:blank:]]*(" sumtypes ")sums(_[^=]+)?\\+?=", $0 ~ "\\)[[:blank:]]*(#.*)?$" {
+ if (!w) {
+ print newsums
+ w++
+ }
+ next
+ }
+
+ 1
+ END { if (!w) print newsums }' PKGBUILD > "${newbuildfile}"; then
+ printf 'Failed to replace the generated checksums'
+ return 1
+ fi
+
+ # overwrite the original PKGBUILD while preserving permissions
+ if ! cat -- "${newbuildfile}" > PKGBUILD; then
+ printf "Failed to write to the PKGBUILD file"
+ return 1
+ fi
+
+ return 0
+}
diff --git a/src/lib/util/term.sh b/src/lib/util/term.sh
index 853dccf..08d044f 100644
--- a/src/lib/util/term.sh
+++ b/src/lib/util/term.sh
@@ -180,3 +180,19 @@ term_spinner_stop() {
# show the cursor after stopping the spinner
term_cursor_show
}
+
+prompt() {
+ local message=$1
+ local answer
+
+ read -r -p "${message} (y/N) " answer
+
+ case "${answer}" in
+ y|Y|yes|Yes|YES)
+ true
+ ;;
+ *)
+ false
+ ;;
+ esac
+}
diff --git a/src/lib/valid-tags.sh b/src/lib/valid-tags.sh
index abca7ef..ef2c5bf 100644
--- a/src/lib/valid-tags.sh
+++ b/src/lib/valid-tags.sh
@@ -4,11 +4,15 @@
:
# shellcheck disable=2034
-_arch=(
+DEVTOOLS_VALID_BINARY_ARCHES=(
pentium4
i686
i486
- x86_64
+)
+
+# shellcheck disable=2034
+DEVTOOLS_VALID_ARCHES=(
+ "${DEVTOOLS_VALID_BINARY_ARCHES[@]}"
any
)
diff --git a/src/lib/version.sh b/src/lib/version.sh
index ac810ae..a18da83 100644
--- a/src/lib/version.sh
+++ b/src/lib/version.sh
@@ -19,6 +19,7 @@ pkgctl_version_usage() {
COMMANDS
check Compares local package versions against upstream
+ setup Automatically detect and setup a basic nvchecker config
upgrade Adjust the PKGBUILD to match the latest upstream version
OPTIONS
@@ -26,6 +27,7 @@ pkgctl_version_usage() {
EXAMPLES
$ ${COMMAND} check libfoo linux libbar
+ $ ${COMMAND} setup libfoo
_EOF_
}
@@ -57,6 +59,14 @@ pkgctl_version() {
pkgctl_version_upgrade "$@"
exit $?
;;
+ setup)
+ _DEVTOOLS_COMMAND+=" $1"
+ shift
+ # shellcheck source=src/lib/version/setup.sh
+ source "${_DEVTOOLS_LIBRARY_DIR}"/lib/version/setup.sh
+ pkgctl_version_setup "$@"
+ exit 0
+ ;;
*)
die "invalid argument: %s" "$1"
;;
diff --git a/src/lib/version/check.sh b/src/lib/version/check.sh
index 35d07e2..4a2b5fa 100644
--- a/src/lib/version/check.sh
+++ b/src/lib/version/check.sh
@@ -108,11 +108,11 @@ pkgctl_version_check() {
term_spinner_start "${status_dir}"
for path in "${pkgbases[@]}"; do
- pushd "${path}" >/dev/null
-
- if [[ ! -f "PKGBUILD" ]]; then
- die "No PKGBUILD found for ${path}"
+ # skip paths that are not directories
+ if [[ ! -d "${path}" ]]; then
+ continue
fi
+ pushd "${path}" >/dev/null
# update the current terminal spinner status
(( ++current_item ))
@@ -124,6 +124,13 @@ pkgctl_version_check() {
"${current_item}" \
"${#pkgbases[@]}"
+ if [[ ! -f "PKGBUILD" ]]; then
+ result="${BOLD}${path}${ALL_OFF}: no PKGBUILD found"
+ failure+=("${result}")
+ popd >/dev/null
+ continue
+ fi
+
# reset common PKGBUILD variables
unset pkgbase pkgname arch source pkgver pkgrel validpgpkeys
# shellcheck source=contrib/makepkg/PKGBUILD.proto
@@ -219,8 +226,8 @@ get_upstream_version() {
opts+=(--keyfile "${keyfile}")
fi
- if ! output=$(nvchecker --file "${config}" --logger json "${opts[@]}" 2>&1 | \
- jq --raw-output 'select(.level != "debug")'); then
+ if ! output=$(GIT_TERMINAL_PROMPT=0 nvchecker --file "${config}" --logger json "${opts[@]}" 2>&1 | \
+ jq --raw-output 'select((.level != "debug") and (.event != "ignoring invalid version"))'); then
printf "failed to run nvchecker: %s" "${output}"
return 1
fi
@@ -260,13 +267,13 @@ nvchecker_check_config() {
done
# check if the config contains a pkgbase section
- if [[ -n ${pkgbase} ]] && ! grep --max-count=1 --extended-regexp --quiet "^\\[\"?${pkgbase}\"?\\]" < "${config}"; then
+ if [[ -n ${pkgbase} ]] && ! grep --max-count=1 --extended-regexp --quiet "^\\[\"?${pkgbase//+/\\+}\"?\\]" < "${config}"; then
printf "missing pkgbase section in %s: %s" "${config}" "${pkgbase}"
return 1
fi
# check if the config contains any section other than pkgbase
- if [[ -n ${pkgbase} ]] && property=$(grep --max-count=1 --perl-regexp "^\\[(?!\"?${pkgbase}\"?\\]).+\\]" < "${config}"); then
+ if [[ -n ${pkgbase} ]] && property=$(grep --max-count=1 --perl-regexp "^\\[(?!\"?${pkgbase//+/\\+}\"?\\]).+\\]" < "${config}"); then
printf "non-pkgbase section not supported in %s: %s" "${config}" "${property}"
return 1
fi
diff --git a/src/lib/version/setup.sh b/src/lib/version/setup.sh
new file mode 100644
index 0000000..cdfbeac
--- /dev/null
+++ b/src/lib/version/setup.sh
@@ -0,0 +1,528 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+[[ -z ${DEVTOOLS_INCLUDE_VERSION_SETUP_SH:-} ]] || return 0
+DEVTOOLS_INCLUDE_VERSION_SETUP_SH=1
+
+_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
+# shellcheck source=src/lib/common.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
+# shellcheck source=src/lib/version/check.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/version/check.sh
+
+source /usr/share/makepkg/util/message.sh
+source /usr/share/makepkg/util/source.sh
+
+set -eo pipefail
+
+
+pkgctl_version_setup_usage() {
+ local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
+ cat <<- _EOF_
+ Usage: ${COMMAND} [OPTIONS] [PKGBASE]...
+
+ Automate the creation of a basic nvchecker configuration file by
+ analyzing the source array specified in the PKGBUILD file of a package.
+
+ If no PKGBASE is specified, the command defaults to using the current
+ working directory.
+
+ OPTIONS
+ -f, --force Overwrite existing nvchecker configuration
+ --prefer-platform-api Prefer platform specific GitHub/GitLab API for complex cases
+ --url URL Derive check target from URL instead of source array
+ --no-check Do not run version check after setup
+ -h, --help Show this help text
+
+ EXAMPLES
+ $ ${COMMAND} neovim vim
+_EOF_
+}
+
+pkgctl_version_setup() {
+ local pkgbases=()
+ local override_url=
+ local run_check=1
+ local force=0
+ local prefer_platform_api=0
+
+ local path ret
+ local checks=()
+
+ while (( $# )); do
+ case $1 in
+ -h|--help)
+ pkgctl_version_setup_usage
+ exit 0
+ ;;
+ -f|--force)
+ force=1
+ shift
+ ;;
+ --prefer-platform-api)
+ prefer_platform_api=1
+ shift
+ ;;
+ --url)
+ (( $# <= 1 )) && die "missing argument for %s" "$1"
+ override_url=$2
+ shift 2
+ ;;
+ --no-check)
+ run_check=0
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ die "invalid argument: %s" "$1"
+ ;;
+ *)
+ pkgbases=("$@")
+ break
+ ;;
+ esac
+ done
+
+ # Check if used without pkgbases in a packaging directory
+ if (( ${#pkgbases[@]} == 0 )); then
+ if [[ -f PKGBUILD ]]; then
+ pkgbases=(".")
+ else
+ pkgctl_version_setup_usage
+ exit 1
+ fi
+ fi
+
+ ret=0
+ for path in "${pkgbases[@]}"; do
+ # skip paths that are not directories
+ if [[ ! -d "${path}" ]]; then
+ continue
+ fi
+
+ pushd "${path}" >/dev/null
+ if nvchecker_setup "${path}" "${force}" "${prefer_platform_api}" "${override_url}"; then
+ checks+=("${path}")
+ else
+ ret=1
+ fi
+ popd >/dev/null
+ done
+
+ # run checks on the setup targets
+ if (( run_check )) && (( ${#checks[@]} >= 1 )); then
+ echo
+ pkgctl_version_check --verbose "${checks[@]}" || true
+ fi
+
+ return $ret
+}
+
+nvchecker_setup() {
+ local path=$1
+ local force=$2
+ local prefer_platform_api=$3
+ local override_url=$4
+ local pkgbase pkgname source source_url proto domain url_parts section body
+
+ if [[ ! -f PKGBUILD ]]; then
+ msg_error "${BOLD}${path}:${ALL_OFF} no PKGBUILD found"
+ return 1
+ fi
+
+ unset body pkgbase pkgname source url
+ # shellcheck source=contrib/makepkg/PKGBUILD.proto
+ if ! . ./PKGBUILD; then
+ msg_error "${BOLD}${path}:${ALL_OFF} failed to source PKGBUILD"
+ return 1
+ fi
+ pkgbase=${pkgbase:-$pkgname}
+
+ # try to guess from url as last try
+ if [[ -n ${url} ]]; then
+ source+=("${url}")
+ fi
+
+ # handle overwrite of existing config
+ if [[ -f .nvchecker.toml ]] && (( ! force )); then
+ msg_warn "${BOLD}${pkgbase}:${ALL_OFF} nvchecker already configured"
+ return 1
+ fi
+
+ # override the source array with a passed URL
+ if [[ -n ${override_url} ]]; then
+ source=("${override_url}")
+ fi
+
+ # skip empty source array
+ if (( ${#source[@]} == 0 )); then
+ msg_error "${BOLD}${pkgbase}:${ALL_OFF} PKGBUILD has no source array"
+ return 1
+ fi
+
+ for source_url in "${source[@]}"; do
+ # Strips out filename::http for example
+ source_url=$(get_url "${source_url}")
+ # discard query fragments
+ source_url=${source_url%\?*}
+ source_url=${source_url%#*}
+
+ # skip patches
+ if [[ ${source_url} == *.patch ]]; then
+ continue
+ fi
+ # skip signatures
+ if [[ ${source_url} == *.asc ]] || [[ ${source_url} == *.sig ]]; then
+ continue
+ fi
+ # skip local files
+ if [[ ${source_url} != *://* ]]; then
+ continue
+ fi
+
+ # split URL segments while avoiding empty element after protocol and newline at the end
+ mapfile -td / url_parts <<< "${source_url/:\/\//\/}/"
+ unset "url_parts[-1]"
+
+ # extract protocol and domain to select the configuration type
+ proto=${url_parts[0]}
+ domain=${url_parts[1]}
+
+ case "${domain}" in
+ gitlab.*)
+ if (( prefer_platform_api )); then
+ body=$(nvchecker_setup_gitlab "${url_parts[@]}")
+ else
+ body=$(nvchecker_setup_git "${url_parts[@]}")
+ fi
+ break
+ ;;
+ github.com)
+ if (( prefer_platform_api )); then
+ body=$(nvchecker_setup_github "${url_parts[@]}")
+ else
+ body=$(nvchecker_setup_git "${url_parts[@]}")
+ fi
+ break
+ ;;
+ codeberg.org)
+ body=$(nvchecker_setup_git "${url_parts[@]}")
+ break
+ ;;
+ pypi.org|pypi.io|files.pythonhosted.org)
+ body=$(nvchecker_setup_pypi "${url_parts[@]}")
+ break
+ ;;
+ hackage.haskell.org)
+ body=$(nvchecker_setup_hackage "${url_parts[@]}")
+ break
+ ;;
+ registry.npmjs.org|npmjs.com|www.npmjs.com)
+ body=$(nvchecker_setup_npm "${url_parts[@]}")
+ break
+ ;;
+ rubygems.org)
+ body=$(nvchecker_setup_rubygems "${url_parts[@]}")
+ break
+ ;;
+ *.cpan.org|*.mcpan.org|*.metacpan.org)
+ body=$(nvchecker_setup_cpan "${url_parts[@]}")
+ break
+ ;;
+ crates.io|*.crates.io)
+ body=$(nvchecker_setup_crates_io "${url_parts[@]}")
+ break
+ ;;
+ *)
+ if [[ ${proto} == git ]] || [[ ${proto} == git+https ]]; then
+ body=$(nvchecker_setup_git "${url_parts[@]}")
+ fi
+ ;;
+ esac
+ done
+
+ if [[ -z "${body}" ]]; then
+ msg_error "${BOLD}${pkgbase}:${ALL_OFF} unable to automatically setup nvchecker"
+ return 1
+ fi
+
+ # escape the section if it contains toml subsection chars
+ section="${pkgbase}"
+ if [[ ${section} == *.* ]] || [[ ${section} == *+* ]]; then
+ section="\"${section}\""
+ fi
+
+ msg_success "${BOLD}${pkgbase}:${ALL_OFF} successfully configured nvchecker"
+ cat > .nvchecker.toml << EOF
+[${section}]
+${body}
+EOF
+}
+
+get_git_url_from_parts() {
+ local url_parts=("$@")
+ local proto=${url_parts[0]#*+}
+ local domain=${url_parts[1]}
+ local url
+ url="${proto}://$(join_by / "${url_parts[@]:1}")"
+
+ case "${domain}" in
+ gitlab.*)
+ url=${url%/-/*/*}
+ [[ ${url} != *.git ]] && url+=.git
+ ;;
+ github.com|codeberg.org)
+ url="${proto}://$(join_by / "${url_parts[@]:1:3}")"
+ [[ ${url} != *.git ]] && url+=.git
+ ;;
+ esac
+
+ printf '%s' "${url}"
+}
+
+# PyPI
+#
+# As Arch python packages don't necessarily match the pypi name, when the
+# provided source url comes from pypi.io or pypi.org try to extract the package
+# name from the (predictable) tarball download url for example:
+#
+# https://pypi.io/packages/source/p/pyflakes/pyflakes-3.1.0.tar.gz
+# https://pypi.io/packages/source/p/pyflakes
+# https://pypi.org/packages/source/b/bleach
+# https://files.pythonhosted.org/packages/source/p/pyflakes
+# https://pypi.org/project/SQLAlchemy/
+nvchecker_setup_pypi() {
+ local url_parts=("$@")
+ local pypi
+
+ if [[ ${url_parts[2]} == packages ]]; then
+ pypi=${url_parts[5]}
+ elif [[ ${url_parts[2]} == project ]]; then
+ pypi=${url_parts[3]}
+ else
+ return 1
+ fi
+
+ cat << EOF
+source = "pypi"
+pypi = "${pypi}"
+EOF
+}
+
+# Git
+#
+# Set up a generic Git source, while removing the proto specific part from makepkg
+#
+# git+https://github.com/prometheus/prometheus.git
+# https://git.foobar.com/some/path/group/project.git
+# https://gitlab.com/sub/group/project/-/archive/8.0.0/packages-8.0.0.tar.gz
+nvchecker_setup_git() {
+ local url_parts=("$@")
+ local url
+ url=$(get_git_url_from_parts "${url_parts[@]}")
+
+ cat << EOF
+source = "git"
+git = "${url}"
+EOF
+
+ # best effort check if the tags are prefixed with v
+ if git_tags_have_version_prefix "${url}"; then
+ echo 'prefix = "v"'
+ fi
+}
+
+git_tags_have_version_prefix() {
+ local url=$1
+ # best effort check if the tags are prefixed with v
+ if ! grep --max-count=1 --quiet --extended-regex 'refs/tags/v[0-9]+[\.0-9]*$' \
+ <(GIT_TERMINAL_PROMPT=0 git ls-remote --quiet --tags "${url}" 2>/dev/null); then
+ return 1
+ fi
+ return 0
+}
+
+# Github
+#
+# We want to know the $org/$project name from the url
+#
+# https://github.com/prometheus/prometheus/archive/v2.49.1.tar.gz
+nvchecker_setup_github() {
+ local url_parts=("$@")
+ local url project
+ if ! url=$(get_git_url_from_parts "${url_parts[@]}"); then
+ return 1
+ fi
+ project=${url#*://*/}
+ project=${project%.git}
+
+ cat << EOF
+source = "github"
+github = "${project}"
+use_max_tag = true
+EOF
+
+ # best effort check if the tags are prefixed with v
+ if git_tags_have_version_prefix "${url}"; then
+ echo 'prefix = "v"'
+ fi
+}
+
+# GitLab
+#
+# We want to know the $org/$project name from the url
+#
+# git+https://gitlab.com/inkscape/inkscape.git#tag=091e20ef0f204eb40ecde54436e1ef934a03d894
+nvchecker_setup_gitlab() {
+ local url_parts=("$@")
+ local url project host
+ if ! url=$(get_git_url_from_parts "${url_parts[@]}"); then
+ return 1
+ fi
+ project=${url#*://*/}
+ project=${project%.git}
+ cat << EOF
+source = "gitlab"
+gitlab = "${project}"
+EOF
+
+ host=${url#*://}
+ host=${host%%/*}
+ if [[ ${host} != gitlab.com ]]; then
+ echo "host = \"${host}\""
+ fi
+
+ echo "use_max_tag = true"
+
+ # best effort check if the tags are prefixed with v
+ if git_tags_have_version_prefix "${url}"; then
+ echo 'prefix = "v"'
+ fi
+}
+
+# Hackage
+#
+# We want to know the project name
+#
+# https://hackage.haskell.org/package/xmonad
+# https://hackage.haskell.org/package/xmonad-0.18.0/xmonad-0.18.0.tar.gz
+# https://hackage.haskell.org/packages/archive/digits/0.3.1/digits-0.3.1.tar.gz
+nvchecker_setup_hackage() {
+ local url_parts=("$@")
+ local hackage
+
+ if [[ ${url_parts[2]} == packages ]]; then
+ hackage=${url_parts[4]}
+ elif [[ ${url_parts[2]} == package ]] && (( ${#url_parts[@]} == 4 )); then
+ hackage=${url_parts[3]}
+ elif [[ ${url_parts[2]} == package ]] && (( ${#url_parts[@]} >= 5 )); then
+ hackage=${url_parts[3]%-*}
+ else
+ return 1
+ fi
+
+ cat << EOF
+source = "hackage"
+hackage = "${hackage}"
+EOF
+}
+
+# NPM
+#
+# We want to know the project name
+#
+# https://registry.npmjs.org/eslint_d/-/eslint_d-12.1.0.tgz
+# https://www.npmjs.com/package/node-gyp
+nvchecker_setup_npm() {
+ local url_parts=("$@")
+ local npm
+
+ if [[ ${url_parts[1]} == registry.npmjs.org ]]; then
+ npm=${url_parts[2]}
+ elif [[ ${url_parts[2]} == package ]] && (( ${#url_parts[@]} == 4 )); then
+ npm=${url_parts[3]}
+ else
+ return 1
+ fi
+
+ cat << EOF
+source = "npm"
+npm = "${npm}"
+EOF
+}
+
+# RubyGems
+#
+# We want to know the project name
+#
+# https://rubygems.org/downloads/polyglot-0.3.5.gem
+# https://rubygems.org/gems/diff-lcs
+nvchecker_setup_rubygems() {
+ local url_parts=("$@")
+ local gem
+
+ if [[ ${url_parts[2]} == downloads ]]; then
+ gem=${url_parts[-1]%-*}
+ elif [[ ${url_parts[2]} == gems ]]; then
+ gem=${url_parts[3]}
+ else
+ return 1
+ fi
+
+ cat << EOF
+source = "gems"
+gems = "${gem}"
+EOF
+}
+
+# CPAN
+#
+# We want to know the project name
+#
+# source = https://search.cpan.org/CPAN/authors/id/C/CO/COSIMO/Locale-PO-1.2.3.tar.gz
+nvchecker_setup_cpan() {
+ local url_parts=("$@")
+ local cpan=${url_parts[-1]}
+ cpan=${cpan%-*}
+
+ cat << EOF
+source = "cpan"
+cpan = "${cpan}"
+EOF
+}
+
+# crates.io
+#
+# We want to know the crate name
+#
+# https://crates.io/api/v1/crates/${pkgname}/${pkgver}/download
+# https://static.crates.io/crates/${pkgname}/$pkgname-$pkgver.crate
+# https://crates.io/crates/git-smash
+nvchecker_setup_crates_io() {
+ local url_parts=("$@")
+ local crate
+
+ if [[ ${url_parts[2]} == crates ]]; then
+ crate=${url_parts[3]}
+ elif [[ ${url_parts[4]} == crates ]]; then
+ crate=${url_parts[5]}
+ else
+ return 1
+ fi
+
+
+ for i in "${!url_parts[@]}"; do
+ if [[ ${url_parts[i]} == crates ]]; then
+ crate=${url_parts[(( i + 1 ))]}
+ fi
+ done
+
+ cat << EOF
+source = "cratesio"
+cratesio = "${crate}"
+EOF
+}
diff --git a/src/lib/version/upgrade.sh b/src/lib/version/upgrade.sh
index e217532..70a4659 100644
--- a/src/lib/version/upgrade.sh
+++ b/src/lib/version/upgrade.sh
@@ -30,14 +30,15 @@ pkgctl_version_upgrade_usage() {
Upon execution, it automatically adjusts the PKGBUILD file, ensuring that the
pkgver field is set to match the latest version available from the upstream
source. In addition to updating the pkgver, this command also resets the pkgrel
- to 1.
+ to 1 and updates checksums.
Outputs a summary of upgraded packages, up-to-date packages, and any check
failures.
OPTIONS
- -v, --verbose Display results including up-to-date versions
- -h, --help Show this help text
+ --no-update-checksums Disable computation and update of the checksums
+ -v, --verbose Display results including up-to-date versions
+ -h, --help Show this help text
EXAMPLES
$ ${COMMAND} neovim vim
@@ -50,6 +51,7 @@ pkgctl_version_upgrade() {
local verbose=0
local exit_code=0
local current_item=0
+ local update_checksums=1
while (( $# )); do
case $1 in
@@ -57,6 +59,10 @@ pkgctl_version_upgrade() {
pkgctl_version_upgrade_usage
exit 0
;;
+ --no-update-checksums)
+ update_checksums=0
+ shift
+ ;;
-v|--verbose)
verbose=1
shift
@@ -99,27 +105,37 @@ pkgctl_version_upgrade() {
term_spinner_start "${status_dir}"
for path in "${pkgbases[@]}"; do
+ # skip paths that aren't directories
+ if [[ ! -d "${path}" ]]; then
+ continue
+ fi
pushd "${path}" >/dev/null
+ (( ++current_item ))
+
if [[ ! -f "PKGBUILD" ]]; then
- die "No PKGBUILD found for ${path}"
+ result="${BOLD}${path}${ALL_OFF}: no PKGBUILD found"
+ failure+=("${result}")
+ popd >/dev/null
+ continue
fi
+ # reset common PKGBUILD variables
+ unset pkgbase pkgname arch source pkgver pkgrel validpgpkeys
+ # shellcheck source=contrib/makepkg/PKGBUILD.proto
+ . ./PKGBUILD
+ pkgbase=${pkgbase:-$pkgname}
+
# update the current terminal spinner status
- (( ++current_item ))
pkgctl_version_upgrade_spinner \
"${status_dir}" \
"${#up_to_date[@]}" \
"${#out_of_date[@]}" \
"${#failure[@]}" \
"${current_item}" \
- "${#pkgbases[@]}"
-
- # reset common PKGBUILD variables
- unset pkgbase pkgname arch source pkgver pkgrel validpgpkeys
- # shellcheck source=contrib/makepkg/PKGBUILD.proto
- . ./PKGBUILD
- pkgbase=${pkgbase:-$pkgname}
+ "${#pkgbases[@]}" \
+ "${pkgbase}" \
+ "query latest version"
if ! result=$(get_upstream_version); then
result="${BOLD}${pkgbase}${ALL_OFF}: ${result}"
@@ -149,6 +165,24 @@ pkgctl_version_upgrade() {
# change the PKGBUILD
pkgbuild_set_pkgver "${upstream_version}"
pkgbuild_set_pkgrel 1
+
+ # download sources and update the checksums
+ if (( update_checksums )); then
+ pkgctl_version_upgrade_spinner \
+ "${status_dir}" \
+ "${#up_to_date[@]}" \
+ "${#out_of_date[@]}" \
+ "${#failure[@]}" \
+ "${current_item}" \
+ "${#pkgbases[@]}" \
+ "${pkgbase}" \
+ "updating checksums"
+
+ if ! result=$(pkgbuild_update_checksums /dev/null); then
+ result="${BOLD}${pkgbase}${ALL_OFF}: failed to update checksums for version ${DARK_GREEN}${upstream_version}${ALL_OFF}"
+ failure+=("${result}")
+ fi
+ fi
fi
popd >/dev/null
@@ -227,6 +261,8 @@ pkgctl_version_upgrade_spinner() {
local failure_count=$4
local current=$5
local total=$6
+ local pkgbase=$7
+ local message=$8
local percentage=$(( 100 * current / total ))
local tmp_file="${status_dir}/tmp"
@@ -239,8 +275,10 @@ pkgctl_version_upgrade_spinner() {
"${failure_count}" > "${tmp_file}"
# print the progress status
- printf "📡 Upgrading: %s/%s [%s] %%spinner%%" \
- "${BOLD}${current}" "${total}" "${percentage}%${ALL_OFF}" \
+ printf "📡 %s: %s\n" \
+ "${pkgbase}" "${BOLD}${message}${ALL_OFF}" >> "${tmp_file}"
+ printf "⌛ Upgrading: %s/%s [%s] %%spinner%%" \
+ "${BOLD}${current}" "${total}" "${percentage}%${ALL_OFF}" \
>> "${tmp_file}"
# swap the status file