Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/src/lib/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util')
-rw-r--r--src/lib/util/git.sh10
-rw-r--r--src/lib/util/makepkg.sh37
-rw-r--r--src/lib/util/pacman.sh12
-rw-r--r--src/lib/util/pkgbuild.sh43
-rw-r--r--src/lib/util/srcinfo.sh69
-rw-r--r--src/lib/util/term.sh182
6 files changed, 351 insertions, 2 deletions
diff --git a/src/lib/util/git.sh b/src/lib/util/git.sh
index c4af662..82e4beb 100644
--- a/src/lib/util/git.sh
+++ b/src/lib/util/git.sh
@@ -7,6 +7,9 @@ DEVTOOLS_INCLUDE_UTIL_GIT_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
+# shellcheck source=src/lib/common.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
+
git_diff_tree() {
local commit=$1
@@ -22,3 +25,10 @@ git_diff_tree() {
"${commit}" \
-- "${path}"
}
+
+git_warmup_ssh_connection() {
+ msg 'Establishing ssh connection to git@%s' "${GITLAB_HOST}"
+ if ! ssh -T "git@${GITLAB_HOST}" >/dev/null; then
+ die 'Failed to establish ssh connection to git@%s' "${GITLAB_HOST}"
+ fi
+}
diff --git a/src/lib/util/makepkg.sh b/src/lib/util/makepkg.sh
new file mode 100644
index 0000000..22df247
--- /dev/null
+++ b/src/lib/util/makepkg.sh
@@ -0,0 +1,37 @@
+#!/hint/bash
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[[ -z ${DEVTOOLS_INCLUDE_UTIL_MAKEPKG_SH:-} ]] || return 0
+DEVTOOLS_INCLUDE_UTIL_MAKEPKG_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/srcinfo.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/srcinfo.sh
+
+
+set -e
+
+makepkg_source_package() {
+ if (( EUID != 0 )); then
+ [[ -z ${WORKDIR:-} ]] && setup_workdir
+ export WORKDIR DEVTOOLS_INCLUDE_COMMON_SH
+ fakeroot -- bash -$- -c "source '${BASH_SOURCE[0]}' && ${FUNCNAME[0]}"
+ return
+ fi
+ (
+ export LIBMAKEPKG_LINT_PKGBUILD_SH=1
+ lint_pkgbuild() { :; }
+
+ export LIBMAKEPKG_SRCINFO_SH=1
+ write_srcinfo() { print_srcinfo; }
+
+ # explicitly instruct makepkg to not sign the source package, even when
+ # the BUILDENV array in makepkg.conf contains 'sign'
+ set +e -- -F --source --nosign
+ # shellcheck source=/usr/bin/makepkg
+ source "$(command -v makepkg)"
+ )
+}
diff --git a/src/lib/util/pacman.sh b/src/lib/util/pacman.sh
index f6c2d5f..620e1a8 100644
--- a/src/lib/util/pacman.sh
+++ b/src/lib/util/pacman.sh
@@ -38,13 +38,21 @@ get_pacman_repo_from_pkgbuild() {
return
fi
+ # update the pacman repo cache if it doesn't exist yet
+ if [[ ! -d "${_DEVTOOLS_PACMAN_CACHE_DIR}" ]]; then
+ update_pacman_repo_cache
+ 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" \
--dbpath "${_DEVTOOLS_PACMAN_CACHE_DIR}" \
- -S \
+ --sync \
+ --nodeps \
+ --nodeps \
--print \
--print-format '%n %r' \
- "${pkgnames[0]}" | grep -E "^${pkgnames[0]} " | awk '{print $2}'
+ "${pkgnames[0]}" 2>/dev/null | awk '$1=="'"${pkgnames[0]}"'"{print $2}'
)
lock_close 10
diff --git a/src/lib/util/pkgbuild.sh b/src/lib/util/pkgbuild.sh
new file mode 100644
index 0000000..ebf8e5f
--- /dev/null
+++ b/src/lib/util/pkgbuild.sh
@@ -0,0 +1,43 @@
+#!/hint/bash
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[[ -z ${DEVTOOLS_INCLUDE_UTIL_PKGBUILD_SH:-} ]] || return 0
+DEVTOOLS_INCLUDE_UTIL_PKGBUILD_SH=1
+
+_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
+
+source /usr/share/makepkg/util/message.sh
+
+set -e
+
+
+# set the pkgver variable in a PKGBUILD
+# assumes that the pkgbuild is sourced to detect the presence of a pkgver function
+pkgbuild_set_pkgver() {
+ local new_pkgver=$1
+ local pkgver=${pkgver}
+
+ if [[ $(type -t pkgver) == function ]]; then
+ # TODO: check if die or warn, if we provide _commit _gitcommit setter maybe?
+ warning 'setting pkgver variable has no effect if the PKGBUILD has a pkgver() function'
+ fi
+
+ if ! grep --extended-regexp --quiet --max-count=1 "^pkgver=${pkgver}$" PKGBUILD; then
+ die "Non-standard pkgver declaration"
+ fi
+ sed --regexp-extended "s|^(pkgver=)${pkgver}$|\1${new_pkgver}|g" --in-place PKGBUILD
+}
+
+# set the pkgrel variable in a PKGBUILD
+# assumes that the pkgbuild is sourced so pkgrel is present
+pkgbuild_set_pkgrel() {
+ local new_pkgrel=$1
+ local pkgrel=${pkgrel}
+
+ if ! grep --extended-regexp --quiet --max-count=1 "^pkgrel=${pkgrel}$" PKGBUILD; then
+ die "Non-standard pkgrel declaration"
+ fi
+ sed --regexp-extended "s|^(pkgrel=)${pkgrel}$|\1${new_pkgrel}|g" --in-place PKGBUILD
+}
+
diff --git a/src/lib/util/srcinfo.sh b/src/lib/util/srcinfo.sh
new file mode 100644
index 0000000..b646dc3
--- /dev/null
+++ b/src/lib/util/srcinfo.sh
@@ -0,0 +1,69 @@
+#!/hint/bash
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[[ -z ${DEVTOOLS_INCLUDE_UTIL_SRCINFO_SH:-} ]] || return 0
+DEVTOOLS_INCLUDE_UTIL_SRCINFO_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/util.sh
+source /usr/share/makepkg/srcinfo.sh
+
+set -eo pipefail
+
+
+print_srcinfo() {
+ local pkgpath=${1:-.}
+ local outdir pkg pid
+ local pids=()
+
+ # source the PKGBUILD
+ # shellcheck source=contrib/makepkg/PKGBUILD.proto
+ . "${pkgpath}"/PKGBUILD
+
+ # run without parallelization for single packages
+ if (( ${#pkgname[@]} == 1 )); then
+ write_srcinfo_content
+ return 0
+ fi
+
+ [[ -z ${WORKDIR:-} ]] && setup_workdir
+ outdir=$(mktemp --directory --tmpdir="${WORKDIR}" pkgctl-srcinfo.XXXXXXXXXX)
+
+ # fork workload for each split pkgname
+ for pkg in "${pkgname[@]}"; do
+ (
+ # deactivate errexit to avoid makepkg abort on grep_function
+ set +e
+ srcinfo_write_package "$pkg" > "${outdir}/${pkg}"
+ )&
+ pids+=($!)
+ done
+
+ # join workload
+ for pid in "${pids[@]}"; do
+ if ! wait "${pid}"; then
+ return 1
+ fi
+ done
+
+ # collect output
+ srcinfo_write_global
+ for pkg in "${pkgname[@]}"; do
+ srcinfo_separate_section
+ cat "${outdir}/${pkg}"
+ done
+}
+
+write_srcinfo_file() {
+ local pkgpath=${1:-.}
+ stat_busy 'Generating .SRCINFO'
+ if ! print_srcinfo "${pkgpath}" > "${pkgpath}"/.SRCINFO; then
+ error 'Failed to write .SRCINFO file'
+ return 1
+ fi
+ stat_done
+}
diff --git a/src/lib/util/term.sh b/src/lib/util/term.sh
new file mode 100644
index 0000000..853dccf
--- /dev/null
+++ b/src/lib/util/term.sh
@@ -0,0 +1,182 @@
+#!/hint/bash
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[[ -z ${DEVTOOLS_INCLUDE_UTIL_TERM_SH:-} ]] || return 0
+DEVTOOLS_INCLUDE_UTIL_TERM_SH=1
+
+set -eo pipefail
+
+
+readonly PKGCTL_TERM_SPINNER_DOTS=Dots
+export PKGCTL_TERM_SPINNER_DOTS
+readonly PKGCTL_TERM_SPINNER_DOTS12=Dots12
+export PKGCTL_TERM_SPINNER_DOTS12
+readonly PKGCTL_TERM_SPINNER_LINE=Line
+export PKGCTL_TERM_SPINNER_LINE
+readonly PKGCTL_TERM_SPINNER_SIMPLE_DOTS_SCROLLING=SimpleDotsScrolling
+export PKGCTL_TERM_SPINNER_SIMPLE_DOTS_SCROLLING
+readonly PKGCTL_TERM_SPINNER_TRIANGLE=Triangle
+export PKGCTL_TERM_SPINNER_TRIANGLE
+readonly PKGCTL_TERM_SPINNER_RANDOM=Random
+export PKGCTL_TERM_SPINNER_RANDOM
+
+readonly PKGCTL_TERM_SPINNER_TYPES=(
+ "${PKGCTL_TERM_SPINNER_DOTS}"
+ "${PKGCTL_TERM_SPINNER_DOTS12}"
+ "${PKGCTL_TERM_SPINNER_LINE}"
+ "${PKGCTL_TERM_SPINNER_SIMPLE_DOTS_SCROLLING}"
+ "${PKGCTL_TERM_SPINNER_TRIANGLE}"
+)
+export PKGCTL_TERM_SPINNER_TYPES
+
+
+term_cursor_hide() {
+ tput civis >&2
+}
+
+term_cursor_show() {
+ tput cnorm >&2
+}
+
+term_cursor_up() {
+ tput cuu1
+}
+
+term_carriage_return() {
+ tput cr
+}
+
+term_erase_line() {
+ tput el
+}
+
+term_erase_lines() {
+ local lines=$1
+
+ local cursor_up erase_line
+ cursor_up=$(term_cursor_up)
+ erase_line="$(term_carriage_return)$(term_erase_line)"
+
+ local prefix=''
+ for _ in $(seq 1 "${lines}"); do
+ printf '%s' "${prefix}${erase_line}"
+ prefix="${cursor_up}"
+ done
+}
+
+_pkgctl_spinner_type=${PKGCTL_TERM_SPINNER_RANDOM}
+term_spinner_set_type() {
+ _pkgctl_spinner_type=$1
+}
+
+# takes a status directory that can be used to dynamically update the spinner
+# by writing to the `status` file inside that directory atomically.
+# replace the placeholder %spinner% with the currently configured spinner type
+term_spinner_start() {
+ local status_dir=$1
+ local parent_pid=$$
+ (
+ local spinner_type=${_pkgctl_spinner_type}
+ local spinner_offset=0
+ local frame_buffer=''
+ local spinner status_message line
+
+ local status_file="${status_dir}/status"
+ local next_file="${status_dir}/next"
+ local drawn_file="${status_dir}/drawn"
+
+ # assign random spinner type
+ if [[ ${spinner_type} == "${PKGCTL_TERM_SPINNER_RANDOM}" ]]; then
+ spinner_type=${PKGCTL_TERM_SPINNER_TYPES[$((RANDOM % ${#PKGCTL_TERM_SPINNER_TYPES[@]}))]}
+ fi
+
+ # select spinner based on the named type
+ case "${spinner_type}" in
+ "${PKGCTL_TERM_SPINNER_DOTS}")
+ spinner=("⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏")
+ update_interval=0.08
+ ;;
+ "${PKGCTL_TERM_SPINNER_DOTS12}")
+ spinner=("⢀⠀" "⡀⠀" "⠄⠀" "⢂⠀" "⡂⠀" "⠅⠀" "⢃⠀" "⡃⠀" "⠍⠀" "⢋⠀" "⡋⠀" "⠍⠁" "⢋⠁" "⡋⠁" "⠍⠉" "⠋⠉" "⠋⠉" "⠉⠙" "⠉⠙" "⠉⠩" "⠈⢙" "⠈⡙" "⢈⠩" "⡀⢙" "⠄⡙" "⢂⠩" "⡂⢘" "⠅⡘" "⢃⠨" "⡃⢐" "⠍⡐" "⢋⠠" "⡋⢀" "⠍⡁" "⢋⠁" "⡋⠁" "⠍⠉" "⠋⠉" "⠋⠉" "⠉⠙" "⠉⠙" "⠉⠩" "⠈⢙" "⠈⡙" "⠈⠩" "⠀⢙" "⠀⡙" "⠀⠩" "⠀⢘" "⠀⡘" "⠀⠨" "⠀⢐" "⠀⡐" "⠀⠠" "⠀⢀" "⠀⡀")
+ update_interval=0.08
+ ;;
+ "${PKGCTL_TERM_SPINNER_LINE}")
+ spinner=("⎯" "\\" "|" "/")
+ update_interval=0.13
+ ;;
+ "${PKGCTL_TERM_SPINNER_SIMPLE_DOTS_SCROLLING}")
+ spinner=(". " ".. " "..." " .." " ." " ")
+ update_interval=0.2
+ ;;
+ "${PKGCTL_TERM_SPINNER_TRIANGLE}")
+ spinner=("◢" "◣" "◤" "◥")
+ update_interval=0.05
+ ;;
+ esac
+
+ # hide the cursor while spinning
+ term_cursor_hide
+
+ # run the spinner as long as the parent process didn't terminate
+ while ps -p "${parent_pid}" &>/dev/null; do
+ # cache the new status template if it exists
+ if mv "${status_file}" "${next_file}" &>/dev/null; then
+ status_message="$(cat "$next_file")"
+ elif [[ -z "${status_message}" ]]; then
+ # wait until we either have a new or cached status
+ sleep 0.05
+ fi
+
+ # fill the frame buffer with the current status
+ local prefix=''
+ while IFS= read -r line; do
+ # replace spinner placeholder
+ line=${line//%spinner%/${spinner[spinner_offset%${#spinner[@]}]}}
+
+ # append the current line to the frame buffer
+ frame_buffer+="${prefix}${line}"
+ prefix=$'\n'
+ done <<< "${status_message}"
+
+ # print current frame buffer
+ echo -n "${frame_buffer}" >&2
+ mv "${next_file}" "${drawn_file}" &>/dev/null ||:
+
+ # setup next frame buffer to clear current content
+ frame_buffer=$(term_erase_lines "$(awk 'END {print NR}' <<< "${status_message}")")
+
+ # advance the spinner animation offset
+ (( ++spinner_offset ))
+
+ # sleep for the spinner update interval
+ sleep "${update_interval}"
+ done
+ )&
+ _pkgctl_spinner_pid=$!
+ disown
+}
+
+term_spinner_stop() {
+ local status_dir=$1
+ local frame_buffer status_file
+
+ # kill the spinner process
+ if ! kill "${_pkgctl_spinner_pid}" > /dev/null 2>&1; then
+ return 1
+ fi
+ unset _pkgctl_spinner_pid
+
+ # acquire last drawn status
+ status_file="${status_dir}/drawn"
+ if [[ ! -f ${status_file} ]]; then
+ return 0
+ fi
+
+ # clear terminal based on last status line
+ frame_buffer=$(term_erase_lines "$(awk 'END {print NR}' < "${status_file}")")
+ echo -n "${frame_buffer}" >&2
+
+ # show the cursor after stopping the spinner
+ term_cursor_show
+}