From 030e6af88063e0a03067cf2bed1e3c6dc070d333 Mon Sep 17 00:00:00 2001
From: Christian Heusel <christian@heusel.eu>
Date: Thu, 6 Jul 2023 15:30:49 +0200
Subject: chore: fix spelling mistake packge -> package

it seems like the mistake was copied to a few locations, this commit
fixes it.

Signed-off-by: Christian Heusel <christian@heusel.eu>
---
 doc/man/pkgctl.1.asciidoc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'doc/man/pkgctl.1.asciidoc')

diff --git a/doc/man/pkgctl.1.asciidoc b/doc/man/pkgctl.1.asciidoc
index 72cda1e..74edf68 100644
--- a/doc/man/pkgctl.1.asciidoc
+++ b/doc/man/pkgctl.1.asciidoc
@@ -33,7 +33,7 @@ pkgctl build::
 	Build packages inside a clean chroot
 
 pkgctl db::
-	Pacman database modification for packge update, move etc
+	Pacman database modification for package update, move etc
 
 pkgctl diff::
 	Compare package files using different modes
-- 
cgit v1.2.3-70-g09d2


From 4673ad6c89bbdca632b22edfc2ef35486b7a635b Mon Sep 17 00:00:00 2001
From: Jelle van der Waa <jelle@archlinux.org>
Date: Sat, 1 Jul 2023 15:21:32 +0200
Subject: feat(search): add subcommand to search across the packaging group

Search for an expression across the GitLab packaging group. To use a
filter, include it in your query. You may use wildcards (*) to use glob
matching. Available filters for the blobs scope: path, extension.

Every usage of the search command must be authenticated. Consult the
'pkgctl auth' command to authenticate with GitLab or view the
authentication status.

This command uses bats for pretty printing the results including line
numbers and syntax highlighting.

Component: pkgctl search
Co-authored-by: Christian Heusel <christian@heusel.eu>
Co-authored-by: Levente Polyak <anthraxx@archlinux.org>
---
 README.md                           |   1 +
 contrib/completion/bash/devtools.in |   9 ++
 contrib/completion/zsh/_devtools.in |   8 ++
 doc/man/pkgctl-search.1.asciidoc    |  58 ++++++++++
 doc/man/pkgctl.1.asciidoc           |   4 +
 src/lib/api/gitlab.sh               | 174 ++++++++++++++++++++++++++--
 src/lib/cache.sh                    |  22 ++++
 src/lib/search.sh                   | 221 ++++++++++++++++++++++++++++++++++++
 src/pkgctl.in                       |   9 ++
 9 files changed, 499 insertions(+), 7 deletions(-)
 create mode 100644 doc/man/pkgctl-search.1.asciidoc
 create mode 100644 src/lib/cache.sh
 create mode 100644 src/lib/search.sh

(limited to 'doc/man/pkgctl.1.asciidoc')

diff --git a/README.md b/README.md
index a1b6d42..6c36a37 100644
--- a/README.md
+++ b/README.md
@@ -67,6 +67,7 @@ Component: pkgctl db remove
 - arch-install-scripts
 - awk
 - bash
+- bats
 - binutils
 - coreutils
 - diffutils
diff --git a/contrib/completion/bash/devtools.in b/contrib/completion/bash/devtools.in
index b974257..155bb7e 100644
--- a/contrib/completion/bash/devtools.in
+++ b/contrib/completion/bash/devtools.in
@@ -139,6 +139,7 @@ _pkgctl_cmds=(
 	diff
 	release
 	repo
+	search
 	version
 )
 _pkgctl_args=(
@@ -331,6 +332,14 @@ _pkgctl_repo_web_args=(
 _pkgctl_repo_web_opts() { _filedir -d; }
 
 
+_pkgctl_search_args=(
+	--json
+	--no-default-filter
+	-h --help
+)
+_pkgctl_search_opts() { :; }
+
+
 _pkgctl_diff_args=(
 	-l --list
 	-d --diffoscope
diff --git a/contrib/completion/zsh/_devtools.in b/contrib/completion/zsh/_devtools.in
index 35ab2dc..120b47a 100644
--- a/contrib/completion/zsh/_devtools.in
+++ b/contrib/completion/zsh/_devtools.in
@@ -139,6 +139,13 @@ _pkgctl_repo_web_args=(
 	'*:git_dir:_files -/'
 )
 
+_pkgctl_search_args=(
+	'--json[Enable printing results in JSON]'
+	'--no-default-filter[Do not apply default filter (like -path:keys/pgp/*.asc)]'
+	'(-h --help)'{-h,--help}'[Display usage]'
+	'1:query'
+)
+
 _arch_nspawn_args=(
 	'-C[Location of a pacman config file]:pacman_config:_files -g "*.conf(.)"'
 	'-M[Location of a makepkg config file]:makepkg_config:_files -g "*.conf(.)"'
@@ -252,6 +259,7 @@ _pkgctl_cmds=(
 	"diff[Compare package files using different modes]"
 	"release[Release step to commit, tag and upload build artifacts]"
 	"repo[Manage Git packaging repositories and their configuration]"
+	"search[Search for an expression across the GitLab packaging group]"
 	"version[Show pkgctl version information]"
 )
 
diff --git a/doc/man/pkgctl-search.1.asciidoc b/doc/man/pkgctl-search.1.asciidoc
new file mode 100644
index 0000000..fb79b88
--- /dev/null
+++ b/doc/man/pkgctl-search.1.asciidoc
@@ -0,0 +1,58 @@
+pkgctl-search(1)
+================
+
+Name
+----
+pkgctl-search - Search for an expression across the GitLab packaging group
+
+Synopsis
+--------
+pkgctl search [OPTIONS] QUERY
+
+Description
+-----------
+
+Search for an expression across the GitLab packaging group.
+
+To use a filter, include it in your query. You may use wildcards (*) to
+use glob matching.
+
+Available filters for the blobs scope: path, extension
+
+Every usage of the search command must be authenticated. Consult the
+'pkgctl auth' command to authenticate with GitLab or view the authentication
+status.
+
+Search Tips
+-----------
+
+	Syntax  Description    Example
+	───────────────────────────────────────
+	"       Exact search   "gem sidekiq"
+	~       Fuzzy search   J~ Doe
+	|       Or             display | banner
+	+       And            display +banner
+	-       Exclude        display -banner
+	*       Partial        bug error 50*
+	\       Escape         \*md
+	#       Issue ID       #23456
+	!       Merge request  !23456
+
+Options
+-------
+
+*--json*::
+	Enable printing results in JSON 
+
+*--no-default-filter*::
+	Do not apply default filter (like -path:keys/pgp/*.asc)
+
+*-h, --help*::
+	Show a help text
+
+See Also
+--------
+
+linkman:pkgctl-auth[1]
+
+include::include/footer.asciidoc[]
diff --git a/doc/man/pkgctl.1.asciidoc b/doc/man/pkgctl.1.asciidoc
index 74edf68..1164561 100644
--- a/doc/man/pkgctl.1.asciidoc
+++ b/doc/man/pkgctl.1.asciidoc
@@ -44,6 +44,9 @@ pkgctl release::
 pkgctl repo::
 	Manage Git packaging repositories and their configuration
 
+pkgctl search::
+	Search for an expression across the GitLab packaging group
+
 pkgctl version::
 	Show pkgctl version information
 
@@ -56,6 +59,7 @@ linkman:pkgctl-db[1]
 linkman:pkgctl-diff[1]
 linkman:pkgctl-release[1]
 linkman:pkgctl-repo[1]
+linkman:pkgctl-search[1]
 linkman:pkgctl-version[1]
 
 include::include/footer.asciidoc[]
diff --git a/src/lib/api/gitlab.sh b/src/lib/api/gitlab.sh
index e5f4237..e4b8a9d 100644
--- a/src/lib/api/gitlab.sh
+++ b/src/lib/api/gitlab.sh
@@ -13,13 +13,63 @@ source "${_DEVTOOLS_LIBRARY_DIR}"/lib/config.sh
 
 set -e
 
+graphql_api_call() {
+	local outfile=$1
+	local request=$2
+	local node_type=$3
+	local data=$4
+	local hasNextPage cursor
+
+	# empty token
+	if [[ -z "${GITLAB_TOKEN}" ]]; then
+		msg_error "  api call failed: No token provided"
+		return 1
+	fi
+
+	[[ -z ${WORKDIR:-} ]] && setup_workdir
+	api_workdir=$(mktemp --tmpdir="${WORKDIR}" --directory pkgctl-gitlab-api.XXXXXXXXXX)
+
+	# normalize graphql data and prepare query
+	data="${data//\"/\\\"}"
+	data='{
+		"query": "'"${data}"'"
+	}'
+	data="${data//$'\t'/ }"
+	data="${data//$'\n'/}"
+
+	cursor=""
+	hasNextPage=true
+	while [[ ${hasNextPage} == true ]]; do
+		data=$(sed -E 's|after: \\"[a-zA-Z0-9]*\\"|after: \\"'"${cursor}"'\\"|' <<< "${data}")
+		result="${api_workdir}/result.${cursor}"
+
+		if ! curl --request "${request}" \
+				--url "https://${GITLAB_HOST}/api/graphql" \
+				--header "Authorization: Bearer ${GITLAB_TOKEN}" \
+				--header "Content-Type: application/json" \
+				--data "${data}" \
+				--output "${result}" \
+				--silent; then
+			msg_error "  api call failed: $(cat "${outfile}")"
+			return 1
+		fi
+
+		hasNextPage=$(jq --raw-output ".data | .${node_type} | .pageInfo | .hasNextPage" < "${result}")
+		cursor=$(jq --raw-output ".data | .${node_type} | .pageInfo | .endCursor" < "${result}")
+
+		cp "${result}" "${api_workdir}/tmp"
+		jq ".data.${node_type}.nodes" "${api_workdir}/tmp" > "${result}"
+	done
+
+	jq --slurp add "${api_workdir}"/result.* > "${outfile}"
+	return 0
+}
 
 gitlab_api_call() {
 	local outfile=$1
 	local request=$2
 	local endpoint=$3
 	local data=${4:-}
-	local error
 
 	# empty token
 	if [[ -z "${GITLAB_TOKEN}" ]]; then
@@ -38,27 +88,102 @@ gitlab_api_call() {
 		return 1
 	fi
 
+	if ! gitlab_check_api_errors "${outfile}"; then
+		return 1
+	fi
+
+	return 0
+}
+
+gitlab_api_call_paged() {
+	local outfile=$1
+	local request=$2
+	local endpoint=$3
+	local data=${4:-}
+	local result header
+
+	# empty token
+	if [[ -z "${GITLAB_TOKEN}" ]]; then
+		msg_error "  api call failed: No token provided"
+		return 1
+	fi
+
+	[[ -z ${WORKDIR:-} ]] && setup_workdir
+	api_workdir=$(mktemp --tmpdir="${WORKDIR}" --directory pkgctl-gitlab-api.XXXXXXXXXX)
+
+	next_page=1
+	while [[ -n "${next_page}" ]]; do
+		result="${api_workdir}/result.${next_page}"
+		header="${api_workdir}/header"
+		if ! curl --request "${request}" \
+				--get \
+				--url "https://${GITLAB_HOST}/api/v4/${endpoint}&per_page=100&page=${next_page}" \
+				--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
+				--header "Content-Type: application/json" \
+				--data-urlencode "${data}" \
+				--dump-header "${header}" \
+				--output "${result}" \
+				--silent; then
+			msg_error "  api call failed: $(cat "${result}")"
+			return 1
+		fi
+
+		if ! gitlab_check_api_errors "${result}"; then
+			return 1
+		fi
+
+		next_page=$(grep "x-next-page" "${header}" | tr -d '\r' | awk '{ print $2 }')
+	done
+
+	jq --slurp add "${api_workdir}"/result.* > "${outfile}"
+	return 0
+}
+
+gitlab_check_api_errors() {
+	local file=$1
+	local error
+
+	# search API only returns an array, no errors
+	if [[ $(jq --raw-output 'type' < "${file}") == "array" ]]; then
+		return 0
+	fi
+
 	# check for general purpose api error
-	if error=$(jq --raw-output --exit-status '.error' < "${outfile}"); then
+	if error=$(jq --raw-output --exit-status '.error' < "${file}"); then
 		msg_error "  api call failed: ${error}"
 		return 1
 	fi
 
 	# check for api specific error messages
-	if ! jq --raw-output --exit-status '.id' < "${outfile}" >/dev/null; then
-		if jq --raw-output --exit-status '.message | keys[]' < "${outfile}" &>/dev/null; then
+	if ! jq --raw-output --exit-status '.id' < "${file}" >/dev/null; then
+		if jq --raw-output --exit-status '.message | keys[]' < "${file}" &>/dev/null; then
 			while read -r error; do
 				msg_error "  api call failed: ${error}"
-			done < <(jq --raw-output --exit-status '.message|to_entries|map("\(.key) \(.value[])")[]' < "${outfile}")
-		elif error=$(jq --raw-output --exit-status '.message' < "${outfile}"); then
+			done < <(jq --raw-output --exit-status '.message|to_entries|map("\(.key) \(.value[])")[]' < "${file}")
+		elif error=$(jq --raw-output --exit-status '.message' < "${file}"); then
 			msg_error "  api call failed: ${error}"
 		fi
 		return 1
 	fi
-
 	return 0
 }
 
+graphql_check_api_errors() {
+	local file=$1
+	local error
+
+	# early exit if we do not have errors
+	if ! jq --raw-output --exit-status '.errors[]' < "${file}" &>/dev/null; then
+		return 0
+	fi
+
+	# check for api specific error messages
+	while read -r error; do
+		msg_error "  api call failed: ${error}"
+	done < <(jq --raw-output --exit-status '.errors[].message' < "${file}")
+	return 1
+}
+
 gitlab_api_get_user() {
 	local outfile username
 
@@ -81,6 +206,23 @@ gitlab_api_get_user() {
 	return 0
 }
 
+gitlab_api_get_project_name_mapping() {
+	local query=$1
+	local outfile
+
+	[[ -z ${WORKDIR:-} ]] && setup_workdir
+	outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
+
+	# query user details
+	if ! graphql_api_call "${outfile}" POST projects "${query}"; then
+		msg_warn "  Invalid token provided?"
+		exit 1
+	fi
+
+	cat "${outfile}"
+	return 0
+}
+
 # Convert arbitrary project names to GitLab valid path names.
 #
 # GitLab has several limitations on project and group names and also maintains
@@ -130,3 +272,21 @@ gitlab_api_create_project() {
 	printf "%s" "${path}"
 	return 0
 }
+
+# TODO: parallelize
+# https://docs.gitlab.com/ee/api/search.html#scope-blobs
+gitlab_api_search() {
+	local search=$1
+	local outfile
+
+	[[ -z ${WORKDIR:-} ]] && setup_workdir
+	outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
+
+	if ! gitlab_api_call_paged "${outfile}" GET "/groups/archlinux%2fpackaging%2fpackages/search?scope=blobs" "search=${search}"; then
+		return 1
+	fi
+
+	cat "${outfile}"
+
+	return 0
+}
diff --git a/src/lib/cache.sh b/src/lib/cache.sh
new file mode 100644
index 0000000..24056fa
--- /dev/null
+++ b/src/lib/cache.sh
@@ -0,0 +1,22 @@
+#!/hint/bash
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[[ -z ${DEVTOOLS_INCLUDE_CACHE_SH:-} ]] || return 0
+DEVTOOLS_INCLUDE_CACHE_SH=1
+
+set -e
+
+readonly XDG_DEVTOOLS_CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/devtools"
+
+get_cache_file() {
+	local filename=$1
+	local path="${XDG_DEVTOOLS_CACHE_DIR}/${filename}"
+
+	mkdir --parents -- "$(dirname -- "$path")"
+	if [[ ! -f ${path} ]]; then
+		touch -- "${path}"
+	fi
+
+	printf '%s' "${path}"
+}
diff --git a/src/lib/search.sh b/src/lib/search.sh
new file mode 100644
index 0000000..cf64db3
--- /dev/null
+++ b/src/lib/search.sh
@@ -0,0 +1,221 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[[ -z ${DEVTOOLS_INCLUDE_SEARCH_SH:-} ]] || return 0
+DEVTOOLS_INCLUDE_SEARCH_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/cache.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/cache.sh
+# shellcheck source=src/lib/api/gitlab.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/api/gitlab.sh
+
+source /usr/share/makepkg/util/message.sh
+
+set -eo pipefail
+
+
+pkgctl_search_usage() {
+	local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
+	cat <<- _EOF_
+		Usage: ${COMMAND} [OPTIONS] QUERY
+
+		Search for an expression across the GitLab packaging group.
+
+		To use a filter, include it in your query. You may use wildcards (*) to
+		use glob matching.
+
+		Available filters for the blobs scope: path, extension
+
+		Every usage of the search command must be authenticated. Consult the
+		'pkgctl auth' command to authenticate with GitLab or view the
+		authentication status.
+
+		SEARCH TIPS
+		    Syntax  Description    Example
+		    ───────────────────────────────────────
+		    "       Exact search   "gem sidekiq"
+		    ~       Fuzzy search   J~ Doe
+		    |       Or             display | banner
+		    +       And            display +banner
+		    -       Exclude        display -banner
+		    *       Partial        bug error 50*
+		    \\       Escape         \\*md
+		    #       Issue ID       #23456
+		    !       Merge request  !23456
+
+		OPTIONS
+		    --json               Enable printing results in JSON
+		    --no-default-filter  Do not apply default filter (like -path:keys/pgp/*.asc)
+		    -h, --help           Show this help text
+
+		EXAMPLES
+		    $ ${COMMAND} linux
+		    $ ${COMMAND} '"pytest -v" +PYTHONPATH'
+_EOF_
+}
+
+pkgctl_search() {
+	if (( $# < 1 )); then
+		pkgctl_search_usage
+		exit 0
+	fi
+
+	# options
+	local search
+	local formatter=pretty
+	local use_default_filter=1
+
+	# variables
+	local default_filter="-path:keys/pgp/*.asc"
+	local graphql_lookup_batch=200
+	local output result query entries from until length
+	local project_name_cache_file project_name_lookup project_ids project_id project_name project_slice
+	local mapping_output path startline data
+
+	while (( $# )); do
+		case $1 in
+			-h|--help)
+				pkgctl_search_usage
+				exit 0
+				;;
+			--json)
+				formatter=json
+				shift
+				;;
+			--no-default-filter)
+				use_default_filter=0
+				shift
+				;;
+			--)
+				shift
+				break
+				;;
+			-*)
+				die "invalid argument: %s" "$1"
+				;;
+			*)
+				break
+				;;
+		esac
+	done
+
+	if (( $# == 0 )); then
+		pkgctl_search_usage
+		exit 1
+	fi
+
+	# assign search parameter
+	search="${*}"
+	if (( use_default_filter )); then
+		search+=" ${default_filter}"
+	fi
+
+	stat_busy "Querying gitlab search api"
+	output=$(gitlab_api_search "${search}")
+	stat_done
+
+	project_name_cache_file=$(get_cache_file gitlab/project_id_to_name)
+	lock 11 "${project_name_cache_file}" "Locking project name cache"
+	mapfile -t project_ids < <(
+		jq --raw-output '[.[].project_id] | unique[]' <<< "${output}" | \
+			grep --invert-match --file <(awk '{ print $1 }' < "${project_name_cache_file}" ))
+
+	stat_busy "Querying project names"
+	local entries="${#project_ids[@]}"
+	local until=0
+	while (( until < entries )); do
+		from=${until}
+		until=$(( until + graphql_lookup_batch ))
+		if (( until > entries )); then
+			until=${entries}
+		fi
+		length=$(( until - from ))
+
+		project_slice=("${project_ids[@]:${from}:${length}}")
+		printf -v projects '"gid://gitlab/Project/%s",' "${project_slice[@]}"
+		query='{
+			projects(after: "" ids: ['"${projects}"']) {
+				pageInfo {
+					startCursor
+					endCursor
+					hasNextPage
+				}
+				nodes {
+					id
+					name
+				}
+			}
+		}'
+		mapping_output=$(gitlab_api_get_project_name_mapping "${query}")
+
+		# update cache
+		while read -r project_id project_name; do
+			printf "%s %s\n" "${project_id}" "${project_name}" >> "${project_name_cache_file}"
+		done < <(jq --raw-output \
+			'.[] | "\(.id | rindex("/") as $lastSlash | .[$lastSlash+1:]) \(.name)"' \
+			<<< "${mapping_output}")
+	done
+	stat_done
+
+	# read project_id to name mapping from cache
+	declare -A project_name_lookup=()
+	while read -r project_id project_name; do
+		project_name_lookup[${project_id}]=${project_name}
+	done < "${project_name_cache_file}"
+
+	# close project name cache lock
+	lock_close 11
+
+	# output mode JSON
+	if [[ ${formatter} == json ]]; then
+		jq --from-file <(
+			for project_id in $(jq '.[].project_id' <<< "${output}"); do
+				project_name=${project_name_lookup[${project_id}]}
+				printf 'map(if .project_id == %s then . + {"project_name": "%s"} else . end) | ' \
+					"${project_id}" "${project_name}"
+			done
+			printf .
+		) <<< "${output}"
+		exit 0
+	fi
+
+	# pretty print each result
+	while read -r result; do
+		# read properties from search result
+		mapfile -t data < <(jq --raw-output ".data" <<< "${result}")
+		{ read -r project_id; read -r path; read -r startline; } < <(
+			jq --raw-output ".project_id, .path, .startline" <<< "${result}"
+		)
+		project_name=${project_name_lookup[${project_id}]}
+
+		# remove trailing newline for multiline results
+		if (( ${#data[@]} > 1 )) && [[ ${data[-1]} == "" ]]; then
+			unset "data[${#data[@]}-1]"
+		fi
+
+		# prepend empty lines to match startline
+		if (( startline > 1 )); then
+			mapfile -t data < <(
+				printf '%.0s\n' $(seq 1 "$(( startline - 1 ))")
+				printf "%s\n" "${data[@]}"
+			)
+		fi
+
+		bat \
+			--file-name="${project_name}/${path}" \
+			--line-range "${startline}:" \
+			--paging=never \
+			--force-colorization \
+			--map-syntax "PKGBUILD:Bourne Again Shell (bash)" \
+			--map-syntax ".SRCINFO:INI" \
+			--map-syntax "*install:Bourne Again Shell (bash)" \
+			--map-syntax "*sysusers*:Bourne Again Shell (bash)" \
+			--map-syntax "*tmpfiles*:Bourne Again Shell (bash)" \
+			--map-syntax "*.hook:INI" \
+			<(printf "%s\n" "${data[@]}")
+	done < <(jq --compact-output '.[]' <<< "${output}")
+}
diff --git a/src/pkgctl.in b/src/pkgctl.in
index ad215ac..10a2348 100644
--- a/src/pkgctl.in
+++ b/src/pkgctl.in
@@ -25,6 +25,7 @@ usage() {
 		    diff    Compare package files using different modes
 		    release Release step to commit, tag and upload build artifacts
 		    repo    Manage Git packaging repositories and their configuration
+		    search  Search for an expression across the GitLab packaging group
 		    version Show pkgctl version information
 
 		OPTIONS
@@ -96,6 +97,14 @@ while (( $# )); do
 			pkgctl_release "$@"
 			exit 0
 			;;
+		search)
+			_DEVTOOLS_COMMAND+=" $1"
+			shift
+			# shellcheck source=src/lib/release.sh
+			source "${_DEVTOOLS_LIBRARY_DIR}"/lib/search.sh
+			pkgctl_search "$@"
+			exit 0
+			;;
 		version|--version|-V)
 			_DEVTOOLS_COMMAND+=" $1"
 			shift
-- 
cgit v1.2.3-70-g09d2


From 2b8033b91132d303603f370a54eef02949703750 Mon Sep 17 00:00:00 2001
From: Christian Heusel <christian@heusel.eu>
Date: Thu, 8 Jun 2023 14:47:29 +0200
Subject: feat(aur): add subcommand to drop a package from the repos to the AUR

Add an aur command to interact with the Arch User Repository including
the drop-from-repo subommand which allows to drop packages from the
official repository to the Arch User Repository in one go.

Related to #143

Component: pkgctl aur drop-from-repo
Co-authored-by: Levente Polyak <anthraxx@archlinux.org>
Signed-off-by: Christian Heusel <christian@heusel.eu>
---
 contrib/completion/bash/devtools.in          |  13 ++-
 contrib/completion/zsh/_devtools.in          |  13 +++
 doc/man/pkgctl-aur-drop-from-repo.1.asciidoc |  41 +++++++
 doc/man/pkgctl-aur.1.asciidoc                |  37 ++++++
 doc/man/pkgctl.1.asciidoc                    |   4 +
 src/lib/aur.sh                               |  65 +++++++++++
 src/lib/aur/drop-from-repo.sh                | 166 +++++++++++++++++++++++++++
 src/lib/common.sh                            |   1 +
 src/pkgctl.in                                |   9 ++
 9 files changed, 348 insertions(+), 1 deletion(-)
 create mode 100644 doc/man/pkgctl-aur-drop-from-repo.1.asciidoc
 create mode 100644 doc/man/pkgctl-aur.1.asciidoc
 create mode 100644 src/lib/aur.sh
 create mode 100644 src/lib/aur/drop-from-repo.sh

(limited to 'doc/man/pkgctl.1.asciidoc')

diff --git a/contrib/completion/bash/devtools.in b/contrib/completion/bash/devtools.in
index 4c7b73a..1fbd46c 100644
--- a/contrib/completion/bash/devtools.in
+++ b/contrib/completion/bash/devtools.in
@@ -135,6 +135,7 @@ complete -F _offload_build offload-build
 
 
 _pkgctl_cmds=(
+	aur
 	auth
 	build
 	db
@@ -264,6 +265,17 @@ _pkgctl_release_args__repo_opts() { _devtools_completions_repo; }
 _pkgctl_release_args_r_opts() { _pkgctl_release_args__repo_opts; }
 _pkgctl_release_opts() { _filedir -d; }
 
+_pkgctl_aur_cmds=(
+	drop-from-repo
+)
+
+_pkgctl_aur_drop_from_repo_args=(
+	--no-disown
+	-f --force
+	-h --help
+)
+_pkgctl_aur_drop_from_repo_opts() { _filedir -d; }
+
 
 _pkgctl_repo_cmds=(
 	clone
@@ -308,7 +320,6 @@ _pkgctl_repo_create_args=(
 	-h --help
 )
 
-
 _pkgctl_repo_switch_args=(
 	--discard-changes
 	-f --force
diff --git a/contrib/completion/zsh/_devtools.in b/contrib/completion/zsh/_devtools.in
index b316346..5b51aff 100644
--- a/contrib/completion/zsh/_devtools.in
+++ b/contrib/completion/zsh/_devtools.in
@@ -96,6 +96,18 @@ _pkgctl_release_args=(
 	'*:git_dir:_files -/'
 )
 
+_pkgctl_aur_cmds=(
+	"pkgctl aur command"
+	"drop-from-repo[Drop a package from the official repository to the AUR]"
+)
+
+_pkgctl_aur_drop_from_repo_args=(
+	'(-f --force)'{-f,--force}'[Force push to the AUR overwriting the remote repository]'
+	'--no-disown[Do not disown the package on the AUR]'
+	'(-h --help)'{-h,--help}'[Display usage]'
+	'*:git_dir:_files -/'
+)
+
 _pkgctl_repo_cmds=(
 	"pkgctl repo command"
 	"clone[Clone a package repository]"
@@ -257,6 +269,7 @@ _devtools_completions_all_packages() {
 
 _pkgctl_cmds=(
 	"pkgctl command"
+	"aur[Interact with the Arch User Repository (AUR)]"
 	"auth[Authenticate with services like GitLab]"
 	"build[Build packages inside a clean chroot]"
 	"db[Pacman database modification for package update, move etc]"
diff --git a/doc/man/pkgctl-aur-drop-from-repo.1.asciidoc b/doc/man/pkgctl-aur-drop-from-repo.1.asciidoc
new file mode 100644
index 0000000..a9d39c6
--- /dev/null
+++ b/doc/man/pkgctl-aur-drop-from-repo.1.asciidoc
@@ -0,0 +1,41 @@
+pkgctl-aur-drop-from-repo(1)
+============================
+
+Name
+----
+pkgctl-aur-drop-from-repo - Drop a package from the official repository to the AUR
+
+Synopsis
+--------
+pkgctl aur drop-from-repo [OPTIONS] [PATH]...
+
+Description
+-----------
+
+Drops a specified package from the official repositories to the Arch User
+Repository.
+
+This command requires a local Git clone of the package repository. It
+reconfigures the repository for AUR compatibility and pushes it to the
+AUR. Afterwards, the package is removed from the official repository.
+
+By default, the package is automatically disowned in the AUR.
+
+Options
+-------
+
+*--no-disown*::
+	Do not disown the package on the AUR
+
+*-f, --force*::
+	Force push to the AUR overwriting the remote repository
+
+*-h, --help*::
+	Show a help text
+
+See Also
+--------
+
+linkman:pkgctl-db-remove[1]
+
+include::include/footer.asciidoc[]
diff --git a/doc/man/pkgctl-aur.1.asciidoc b/doc/man/pkgctl-aur.1.asciidoc
new file mode 100644
index 0000000..d69124a
--- /dev/null
+++ b/doc/man/pkgctl-aur.1.asciidoc
@@ -0,0 +1,37 @@
+pkgctl-aur(1)
+=============
+
+Name
+----
+pkgctl-aur - Interact with the Arch User Repository (AUR)
+
+Synopsis
+--------
+pkgctl aur [OPTIONS] [SUBCOMMAND]
+
+Description
+-----------
+
+Provides a suite of tools designed for managing and interacting with the Arch
+User Repository (AUR). It simplifies various tasks related to AUR, including
+importing repositories, managing packages, and transitioning packages between
+the official repositories and the AUR.
+
+Options
+-------
+
+*-h, --help*::
+	Show a help text
+
+Subcommands
+-----------
+
+pkgctl aur drop-from-repo::
+	Drop a package from the official repository to the AUR
+
+See Also
+--------
+
+linkman:pkgctl-aur-drop-from-repo[1]
+
+include::include/footer.asciidoc[]
diff --git a/doc/man/pkgctl.1.asciidoc b/doc/man/pkgctl.1.asciidoc
index 1164561..24b0613 100644
--- a/doc/man/pkgctl.1.asciidoc
+++ b/doc/man/pkgctl.1.asciidoc
@@ -26,6 +26,9 @@ Options
 Subcommands
 -----------
 
+pkgctl aur::
+	Interact with the Arch User Repository
+
 pkgctl auth::
 	Authenticate with services like GitLab
 
@@ -53,6 +56,7 @@ pkgctl version::
 See Also
 --------
 
+linkman:pkgctl-aur[1]
 linkman:pkgctl-auth[1]
 linkman:pkgctl-build[1]
 linkman:pkgctl-db[1]
diff --git a/src/lib/aur.sh b/src/lib/aur.sh
new file mode 100644
index 0000000..24cbb62
--- /dev/null
+++ b/src/lib/aur.sh
@@ -0,0 +1,65 @@
+#!/hint/bash
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[[ -z ${DEVTOOLS_INCLUDE_AUR_SH:-} ]] || return 0
+DEVTOOLS_INCLUDE_AUR_SH=1
+
+_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
+
+set -eo pipefail
+
+
+pkgctl_aur_usage() {
+	local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
+	cat <<- _EOF_
+		Usage: ${COMMAND} [COMMAND] [OPTIONS]
+
+		Interact with the Arch User Repository (AUR).
+
+		Provides a suite of tools designed for managing and interacting with the Arch
+		User Repository (AUR). It simplifies various tasks related to AUR, including
+		importing repositories, managing packages, and transitioning packages between
+		the official repositories and the AUR.
+
+		COMMANDS
+		    drop-from-repo    Drop a package from the official repository to the AUR
+
+		OPTIONS
+		    -h, --help        Show this help text
+
+		EXAMPLES
+		    $ ${COMMAND} drop-from-repo libfoo
+_EOF_
+}
+
+pkgctl_aur() {
+	if (( $# < 1 )); then
+		pkgctl_aur_usage
+		exit 0
+	fi
+
+	# option checking
+	while (( $# )); do
+		case $1 in
+			-h|--help)
+				pkgctl_aur_usage
+				exit 0
+				;;
+			drop-from-repo)
+				_DEVTOOLS_COMMAND+=" $1"
+				shift
+				# shellcheck source=src/lib/aur/drop-from-repo.sh
+				source "${_DEVTOOLS_LIBRARY_DIR}"/lib/aur/drop-from-repo.sh
+				pkgctl_aur_drop_from_repo "$@"
+				exit 0
+				;;
+			-*)
+				die "invalid argument: %s" "$1"
+				;;
+			*)
+				die "invalid command: %s" "$1"
+				;;
+		esac
+	done
+}
diff --git a/src/lib/aur/drop-from-repo.sh b/src/lib/aur/drop-from-repo.sh
new file mode 100644
index 0000000..d70b559
--- /dev/null
+++ b/src/lib/aur/drop-from-repo.sh
@@ -0,0 +1,166 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[[ -z ${DEVTOOLS_INCLUDE_AUR_DROP_FROM_REPO_SH:-} ]] || return 0
+DEVTOOLS_INCLUDE_AUR_DROP_FROM_REPO_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/db/remove.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/db/remove.sh
+# shellcheck source=src//lib/util/pacman.sh
+source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/pacman.sh
+
+source /usr/share/makepkg/util/message.sh
+
+set -eo pipefail
+
+
+pkgctl_aur_drop_from_repo_usage() {
+	local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
+	cat <<- _EOF_
+		Usage: ${COMMAND} [OPTIONS] [PATH]...
+
+		Drops a specified package from the official repositories to the Arch
+		User Repository.
+
+		This command requires a local Git clone of the package repository. It
+		reconfigures the repository for AUR compatibility and pushes it to the
+		AUR. Afterwards, the package is removed from the official repository.
+
+		By default, the package is automatically disowned in the AUR.
+
+		OPTIONS
+		    --no-disown    Do not disown the package on the AUR
+		    -f, --force    Force push to the AUR overwriting the remote repository
+		    -h, --help     Show this help text
+
+		EXAMPLES
+		    $ ${COMMAND} foo
+		    $ ${COMMAND} --no-disown --force
+_EOF_
+}
+
+pkgctl_aur_drop_from_repo() {
+	# options
+	local paths=()
+	local DISOWN=1
+	local FORCE=0
+
+	# variables
+	local path realpath pkgbase pkgrepo remote_url
+
+	while (( $# )); do
+		case $1 in
+			-h|--help)
+				pkgctl_aur_drop_from_repo_usage
+				exit 0
+				;;
+			--no-disown)
+				DISOWN=0
+				shift
+				;;
+			-f|--force)
+				FORCE=1
+				shift
+				;;
+			--)
+				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
+		if [[ -f PKGBUILD ]]; then
+			paths=(".")
+		else
+			pkgctl_aur_drop_from_repo_usage
+			exit 1
+		fi
+	fi
+
+	for path in "${paths[@]}"; do
+		if ! realpath=$(realpath -e "${path}"); then
+			die "No such directory: ${path}"
+		fi
+
+		pkgbase=$(basename "${realpath}")
+		pkgbase=${pkgbase%.git}
+
+		if [[ ! -d "${path}/.git" ]]; then
+			die "Not a Git repository: ${path}"
+		fi
+
+		pushd "${path}" >/dev/null
+
+		if [[ ! -f PKGBUILD ]]; then
+			die 'PKGBUILD not found in %s' "${path}"
+		fi
+
+		msg "Dropping ${pkgbase} to the AUR"
+
+		remote_url="${AUR_URL_SSH}:${pkgbase}.git"
+		if ! git remote add origin "${remote_url}" &>/dev/null; then
+			git remote set-url origin "${remote_url}"
+		fi
+
+		# move the main branch to master
+		if [[ $(git symbolic-ref --quiet --short HEAD) == main ]]; then
+			git branch --move master
+			git config branch.master.merge refs/heads/master
+			git remote set-head origin master
+		fi
+
+		# auto generate .SRCINFO if not already present
+		if [[ -z "$(git ls-tree -r HEAD --name-only .SRCINFO)" ]]; then
+			stat_busy 'Generating .SRCINFO'
+			makepkg --printsrcinfo > .SRCINFO
+			stat_done
+
+			git add --force -- .SRCINFO
+			git commit --quiet --message "Adding .SRCINFO" -- .SRCINFO
+		fi
+
+		msg "Pushing ${pkgbase} to the AUR"
+		if (( FORCE )); then
+			AUR_OVERWRITE=1 \
+				GIT_SSH_COMMAND="ssh -o SendEnv=AUR_OVERWRITE" \
+				git push --force origin master
+		else
+			git push origin master
+		fi
+
+		if (( DISOWN )); then
+			msg "Disowning ${pkgbase} on the AUR"
+			# shellcheck disable=SC2029
+			ssh "${AUR_URL_SSH}" disown "${pkgbase}"
+		fi
+
+		# auto-detection of the repo to remove from
+		if ! pkgrepo=$(get_pacman_repo_from_pkgbuild PKGBUILD); then
+			die 'Failed to get pacman repo'
+		fi
+
+		msg "Deleting ${pkgbase} from the official repository"
+		if [[ -z "${pkgrepo}" ]]; then
+			warning 'Did not find %s in any repository, please delete manually' "${pkgbase}"
+		else
+			msg2 "  repo: ${pkgrepo}"
+			pkgctl_db_remove "${pkgrepo}" "${pkgbase}"
+		fi
+
+		popd >/dev/null
+	done
+}
diff --git a/src/lib/common.sh b/src/lib/common.sh
index 17b91bc..a93e906 100644
--- a/src/lib/common.sh
+++ b/src/lib/common.sh
@@ -29,6 +29,7 @@ export GIT_PACKAGING_URL_SSH="git@${GITLAB_HOST}:${GIT_PACKAGING_NAMESPACE}"
 export GIT_PACKAGING_URL_HTTPS="https://${GITLAB_HOST}/${GIT_PACKAGING_NAMESPACE}"
 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
 
 # check if messages are to be printed using color
 if [[ -t 2 && "$TERM" != dumb ]] || [[ ${DEVTOOLS_COLOR} == always ]]; then
diff --git a/src/pkgctl.in b/src/pkgctl.in
index 10a2348..070e0cd 100644
--- a/src/pkgctl.in
+++ b/src/pkgctl.in
@@ -19,6 +19,7 @@ usage() {
 		Unified command-line frontend for devtools.
 
 		COMMANDS
+		    aur     Interact with the Arch User Repository
 		    auth    Authenticate with services like GitLab
 		    build   Build packages inside a clean chroot
 		    db      Pacman database modification for package update, move etc
@@ -51,6 +52,14 @@ while (( $# )); do
 			usage
 			exit 0
 			;;
+		aur)
+			_DEVTOOLS_COMMAND+=" $1"
+			shift
+			# shellcheck source=src/lib/aur.sh
+			source "${_DEVTOOLS_LIBRARY_DIR}"/lib/aur.sh
+			pkgctl_aur "$@"
+			exit 0
+			;;
 		build)
 			_DEVTOOLS_COMMAND+=" $1"
 			shift
-- 
cgit v1.2.3-70-g09d2


From e6f7aa395fabc7eca1ee4d93e454d2551cea505c Mon Sep 17 00:00:00 2001
From: Jelle van der Waa <jelle@archlinux.org>
Date: Sun, 5 Nov 2023 16:11:12 +0100
Subject: feat(version): introduce version check subcommand

The version subcommand handles pkgver related commands, the first
subcommand being `check`. Check runs nvchecker if a `.nvchecker.toml`
file exists and compares the current pkgver with the latest release.

Introduces nvchecker as optional dependency which has to be installed in
order to use this particular subcommand.

BREAKING CHANGE: formerly pkgctl version would output the version of the
pkgctl tool, now it is used as a version related subcommand.

Fixes #140

Component: pkgctl version
Component: pkgctl version check
Co-authored-by: Christian Heusel <christian@heusel.eu>
---
 README.md                               |  1 +
 contrib/completion/bash/devtools.in     |  9 +++
 contrib/completion/zsh/_devtools.in     | 10 +++-
 doc/man/pkgctl-version-check.1.asciidoc | 35 ++++++++++++
 doc/man/pkgctl-version.1.asciidoc       | 18 +++++-
 doc/man/pkgctl.1.asciidoc               |  2 +-
 src/lib/version.sh                      | 56 +++++++++++++++++++
 src/lib/version/check.sh                | 97 +++++++++++++++++++++++++++++++++
 src/lib/version/version.sh              | 47 ----------------
 src/pkgctl.in                           | 20 +++++--
 10 files changed, 238 insertions(+), 57 deletions(-)
 create mode 100644 doc/man/pkgctl-version-check.1.asciidoc
 create mode 100644 src/lib/version.sh
 create mode 100644 src/lib/version/check.sh
 delete mode 100644 src/lib/version/version.sh

(limited to 'doc/man/pkgctl.1.asciidoc')

diff --git a/README.md b/README.md
index ca761db..8b85bd9 100644
--- a/README.md
+++ b/README.md
@@ -89,6 +89,7 @@ Component: pkgctl db remove
 ### Optional Dependencies
 
 - bats (pretty printing)
+- nvchecker (version checking)
 
 ### Development Dependencies
 
diff --git a/contrib/completion/bash/devtools.in b/contrib/completion/bash/devtools.in
index 1fbd46c..f1084ab 100644
--- a/contrib/completion/bash/devtools.in
+++ b/contrib/completion/bash/devtools.in
@@ -337,6 +337,15 @@ _pkgctl_repo_switch_opts() {
 	fi
 }
 
+_pkgctl_version_cmds=(
+	check
+)
+
+_pkgctl_version_check_args=(
+	-h --help
+)
+
+_pkgctl_version_check_opts() { _filedir -d; }
 
 _pkgctl_repo_web_args=(
 	--print
diff --git a/contrib/completion/zsh/_devtools.in b/contrib/completion/zsh/_devtools.in
index 5b51aff..575c327 100644
--- a/contrib/completion/zsh/_devtools.in
+++ b/contrib/completion/zsh/_devtools.in
@@ -277,7 +277,7 @@ _pkgctl_cmds=(
 	"release[Release step to commit, tag and upload build artifacts]"
 	"repo[Manage Git packaging repositories and their configuration]"
 	"search[Search for an expression across the GitLab packaging group]"
-	"version[Show pkgctl version information]"
+	"version[Package version related commands]"
 )
 
 _pkgctl_args=(
@@ -285,8 +285,14 @@ _pkgctl_args=(
 	'(-h --help)'{-h,--help}'[Display usage]'
 )
 
-_pkgctl_version_args=(
+_pkgctl_version_cmds=(
+	"pkgctl version command"
+	"check[Check if there is an new upstream version available]"
+)
+
+_pkgctl_version_check_args=(
 	'(-h --help)'{-h,--help}'[Display usage]'
+	'*:git_dir:_files -/'
 )
 
 _pkgctl_diff_args=("${_diffpkg_args[@]}")
diff --git a/doc/man/pkgctl-version-check.1.asciidoc b/doc/man/pkgctl-version-check.1.asciidoc
new file mode 100644
index 0000000..97d8c47
--- /dev/null
+++ b/doc/man/pkgctl-version-check.1.asciidoc
@@ -0,0 +1,35 @@
+pkgctl-version-check(1)
+=======================
+
+Name
+----
+pkgctl-version-check - Check if there is an new upstream version available
+
+Synopsis
+--------
+pkgctl version check [OPTIONS] [PKGBASE...]
+
+Description
+-----------
+
+Uses nvchecker, a .nvchecker.toml file and the version specified in the current
+PKGBUILDs pkgver to check if there is a newer package version available to
+which this package could possibly be upgraded.
+
+The current working directory is used if no PKGBASE is specified.
+
+See the section on configuration files in **nvchecker**(1) for possible options
+which can be utilized in .nvchecker.toml.
+
+Options
+-------
+
+*-h, --help*::
+	Show a help text
+
+See Also
+--------
+
+linkman:nvchecker[1]
+
+include::include/footer.asciidoc[]
diff --git a/doc/man/pkgctl-version.1.asciidoc b/doc/man/pkgctl-version.1.asciidoc
index 9beebf5..53a72f3 100644
--- a/doc/man/pkgctl-version.1.asciidoc
+++ b/doc/man/pkgctl-version.1.asciidoc
@@ -3,16 +3,17 @@ pkgctl-version(1)
 
 Name
 ----
-pkgctl-version - Show pkgctl version information
+pkgctl-version - Package version related commands.
+
 
 Synopsis
 --------
-pkgctl version [OPTIONS]
+pkgctl version [OPTIONS] [SUBCOMMAND]
 
 Description
 -----------
 
-Shows the current version information of pkgctl.
+Package version related commands such as checking if a package is out of date.
 
 Options
 -------
@@ -20,4 +21,15 @@ Options
 *-h, --help*::
 	Show a help text
 
+Subcommands
+-----------
+
+pkgctl version check::
+	Check if there is an new upstream version available
+
+See Also
+--------
+
+linkman:pkgctl-version-check[1]
+
 include::include/footer.asciidoc[]
diff --git a/doc/man/pkgctl.1.asciidoc b/doc/man/pkgctl.1.asciidoc
index 24b0613..929ae9a 100644
--- a/doc/man/pkgctl.1.asciidoc
+++ b/doc/man/pkgctl.1.asciidoc
@@ -51,7 +51,7 @@ pkgctl search::
 	Search for an expression across the GitLab packaging group
 
 pkgctl version::
-	Show pkgctl version information
+	Package version related commands
 
 See Also
 --------
diff --git a/src/lib/version.sh b/src/lib/version.sh
new file mode 100644
index 0000000..826b306
--- /dev/null
+++ b/src/lib/version.sh
@@ -0,0 +1,56 @@
+#!/hint/bash
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[[ -z ${DEVTOOLS_INCLUDE_VERSION_SH:-} ]] || return 0
+DEVTOOLS_INCLUDE_VERSION_SH=1
+
+_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
+
+set -e
+
+
+pkgctl_version_usage() {
+	local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
+    cat <<- _EOF_
+		Usage: ${COMMAND} [COMMAND] [OPTIONS]
+
+		Package version related commands.
+
+		COMMANDS
+		    check    Check if there is a newer version availble
+
+		OPTIONS
+		    -h, --help    Show this help text
+
+		EXAMPLES
+		    $ ${COMMAND} check libfoo linux libbar
+_EOF_
+}
+
+pkgctl_version() {
+	if (( $# < 1 )); then
+		pkgctl_version_usage
+		exit 0
+	fi
+
+	while (( $# )); do
+		case $1 in
+			-h|--help)
+				pkgctl_version_usage
+				exit 0
+				;;
+			check)
+				_DEVTOOLS_COMMAND+=" $1"
+				shift
+				# shellcheck source=src/lib/version/check.sh
+				source "${_DEVTOOLS_LIBRARY_DIR}"/lib/version/check.sh
+				pkgctl_version_check "$@"
+				exit 0
+				;;
+			*)
+				die "invalid argument: %s" "$1"
+				;;
+		esac
+	done
+}
diff --git a/src/lib/version/check.sh b/src/lib/version/check.sh
new file mode 100644
index 0000000..f6619c2
--- /dev/null
+++ b/src/lib/version/check.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+[[ -z ${DEVTOOLS_INCLUDE_VERSION_CHECK_SH:-} ]] || return 0
+DEVTOOLS_INCLUDE_VERSION_CHECK_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 -e
+
+pkgctl_version_check_usage() {
+	local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
+	cat <<- _EOF_
+		Usage: ${COMMAND} [OPTIONS] [PKGBASE]...
+
+		Uses nvchecker, a .nvchecker.toml file and the current PKGBUILD
+		pkgver to check if there is a newer package version available.
+
+		The current working directory is used if no PKGBASE is specified.
+
+		OPTIONS
+		    -h, --help          Show this help text
+
+		EXAMPLES
+		    $ ${COMMAND} neovim vim
+_EOF_
+}
+
+pkgctl_version_check() {
+	local path
+	local pkgbases=()
+
+	while (( $# )); do
+		case $1 in
+			-h|--help)
+				pkgctl_version_check_usage
+				exit 0
+				;;
+			--)
+				shift
+				break
+				;;
+			-*)
+				die "invalid argument: %s" "$1"
+				;;
+			*)
+				pkgbases=("$@")
+				break
+				;;
+		esac
+	done
+
+	if ! command -v nvchecker &>/dev/null; then
+		die "The \"$_DEVTOOLS_COMMAND\" command requires 'nvchecker'"
+	fi
+
+	# Check if used without pkgbases in a packaging directory
+	if (( ${#pkgbases[@]} == 0 )); then
+		if [[ -f PKGBUILD ]]; then
+			pkgbases=(".")
+		else
+			pkgctl_version_check_usage
+			exit 1
+		fi
+	fi
+
+	for path in "${pkgbases[@]}"; do
+		pushd "${path}" >/dev/null
+		run_nvchecker "${path}"
+		popd >/dev/null
+	done
+}
+
+run_nvchecker() {
+	local path=$1
+	local pkgbase latest_version
+
+	if [[ ! -f ".nvchecker.toml" || ! -f "PKGBUILD" ]]; then
+		die "No .nvchecker.toml or PKGBUILD found for ${path}"
+		exit 1
+	fi
+
+	# TODO: parse .SRCINFO file
+	# shellcheck source=contrib/makepkg/PKGBUILD.proto
+	. ./PKGBUILD
+	pkgbase=${pkgbase:-$pkgname}
+
+	latest_version=$(nvchecker -c .nvchecker.toml --logger json | jq --raw-output 'select( .version ) | .version')
+	if (( $(vercmp "${latest_version}" "${pkgver}") > 0 )); then
+		msg2 "New ${pkgbase} version ${latest_version} is available upstream"
+	fi
+}
diff --git a/src/lib/version/version.sh b/src/lib/version/version.sh
deleted file mode 100644
index d00a460..0000000
--- a/src/lib/version/version.sh
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/hint/bash
-#
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-[[ -z ${DEVTOOLS_INCLUDE_VERSION_SH:-} ]] || return 0
-DEVTOOLS_INCLUDE_VERSION_SH=1
-
-_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
-
-source /usr/share/makepkg/util/message.sh
-
-set -e
-
-
-pkgctl_version_usage() {
-	local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
-    cat <<- _EOF_
-		Usage: ${COMMAND} [OPTIONS]
-
-		Shows the current version information of pkgctl
-
-		OPTIONS
-		    -h, --help    Show this help text
-_EOF_
-}
-
-pkgctl_version_print() {
-	cat <<- _EOF_
-		pkgctl @buildtoolver@
-_EOF_
-}
-
-pkgctl_version() {
-	while (( $# )); do
-		case $1 in
-			-h|--help)
-				pkgctl_version_usage
-				exit 0
-				;;
-			*)
-				die "invalid argument: %s" "$1"
-				;;
-		esac
-	done
-
-	pkgctl_version_print
-}
diff --git a/src/pkgctl.in b/src/pkgctl.in
index 070e0cd..9deb4b2 100644
--- a/src/pkgctl.in
+++ b/src/pkgctl.in
@@ -27,7 +27,7 @@ usage() {
 		    release Release step to commit, tag and upload build artifacts
 		    repo    Manage Git packaging repositories and their configuration
 		    search  Search for an expression across the GitLab packaging group
-		    version Show pkgctl version information
+		    version Package version related commands
 
 		OPTIONS
 		    -h, --help     Show this help text
@@ -39,6 +39,12 @@ if (( $# < 1 )); then
 	exit 1
 fi
 
+pkgctl_version_print() {
+	cat <<- _EOF_
+		pkgctl @buildtoolver@
+_EOF_
+}
+
 export _DEVTOOLS_COMMAND='pkgctl'
 
 setup_workdir
@@ -114,14 +120,20 @@ while (( $# )); do
 			pkgctl_search "$@"
 			exit 0
 			;;
-		version|--version|-V)
+		version)
 			_DEVTOOLS_COMMAND+=" $1"
 			shift
-			# shellcheck source=src/lib/version/version.sh
-			source "${_DEVTOOLS_LIBRARY_DIR}"/lib/version/version.sh
+			# shellcheck source=src/lib/version.sh
+			source "${_DEVTOOLS_LIBRARY_DIR}"/lib/version.sh
 			pkgctl_version "$@"
 			exit 0
 			;;
+		--version|-V)
+			_DEVTOOLS_COMMAND+=" $1"
+			shift
+			pkgctl_version_print
+			exit 0
+			;;
 		*)
 			die "invalid command: %s" "$1"
 			;;
-- 
cgit v1.2.3-70-g09d2


From 4d72f4560c3a64b38dcdb705ec633c3c2ba1f20c Mon Sep 17 00:00:00 2001
From: Levente Polyak <anthraxx@archlinux.org>
Date: Fri, 19 Jan 2024 01:57:18 +0100
Subject: doc(version): extensively revamp documentation for version
 subcommands

Enhanced and overhauled the documentation for the 'version' subcommand.
The improvements include comprehensive details on the subcommand's
behavior, usage, and a variety of scenarios it handles.

Added a central section within the pkgctl-version manpage that documents
the nvchecker configuration and rules.

Component: pkgctl version
Singled-off-by: Levente Polyak <anthraxx@archlinux.org>
---
 contrib/completion/zsh/_devtools.in       |  6 +++---
 doc/man/pkgctl-version-check.1.asciidoc   | 29 +++++++++++++++++------------
 doc/man/pkgctl-version-upgrade.1.asciidoc | 24 +++++++++++++++++++-----
 doc/man/pkgctl-version.1.asciidoc         | 29 +++++++++++++++++++++++++----
 doc/man/pkgctl.1.asciidoc                 |  2 +-
 src/lib/version.sh                        |  6 +++---
 src/lib/version/check.sh                  | 12 +++++++-----
 src/lib/version/upgrade.sh                | 12 ++++++++----
 src/pkgctl.in                             |  2 +-
 9 files changed, 84 insertions(+), 38 deletions(-)

(limited to 'doc/man/pkgctl.1.asciidoc')

diff --git a/contrib/completion/zsh/_devtools.in b/contrib/completion/zsh/_devtools.in
index b427d20..34f90c6 100644
--- a/contrib/completion/zsh/_devtools.in
+++ b/contrib/completion/zsh/_devtools.in
@@ -277,7 +277,7 @@ _pkgctl_cmds=(
 	"release[Release step to commit, tag and upload build artifacts]"
 	"repo[Manage Git packaging repositories and their configuration]"
 	"search[Search for an expression across the GitLab packaging group]"
-	"version[Package version related commands]"
+	"version[Check and manage package versions against upstream]"
 )
 
 _pkgctl_args=(
@@ -287,8 +287,8 @@ _pkgctl_args=(
 
 _pkgctl_version_cmds=(
 	"pkgctl version command"
-	"check[Check if there is an new upstream version available]"
-	"upgrade[Upgrade the PKGBUILD according to the latest available upstream version]"
+	"check[Compares local package versions against upstream versions]"
+	"upgrade[Adjust the PKGBUILD to match the latest upstream version]"
 )
 
 _pkgctl_version_check_args=(
diff --git a/doc/man/pkgctl-version-check.1.asciidoc b/doc/man/pkgctl-version-check.1.asciidoc
index fa5401f..2543bcb 100644
--- a/doc/man/pkgctl-version-check.1.asciidoc
+++ b/doc/man/pkgctl-version-check.1.asciidoc
@@ -3,7 +3,7 @@ pkgctl-version-check(1)
 
 Name
 ----
-pkgctl-version-check - Check if there is an new upstream version available
+pkgctl-version-check - Compares local package versions against upstream
 
 Synopsis
 --------
@@ -12,20 +12,24 @@ pkgctl version check [OPTIONS] [PKGBASE...]
 Description
 -----------
 
-Uses nvchecker, a .nvchecker.toml file and the version specified in the current
-PKGBUILDs pkgver to check if there is a newer package version available to
-which this package could possibly be upgraded.
+Compares the versions of packages in the local packaging repository against
+their latest upstream versions.
 
-The current working directory is used if no PKGBASE is specified.
+Upon execution, it generates a grouped list that provides detailed insights
+into each package's status. For each package, it displays the current local
+version alongside the latest version available upstream.
+
+Outputs a summary of up-to-date packages, out-of-date packages, and any check
+failures.
+
+This simplifies the maintenance of PKGBUILD files, reducing the manual effort
+required to track version changes from upstream sources.
 
 Configuration
 -------------
 
-To provide GitHub or GitLab tokens to nvchecker, a `keyfile.toml` will be
-supplied from `$XDG_CONFIG_HOME`/nvchecker.
-
-See the section on configuration files in linkman:nvchecker[1] for possible options
-which can be utilized in .nvchecker.toml.
+Uses linkman:nvchecker[1] and a `.nvchecker.toml` file located alongside the
+PKGBUILD. Refer to the configuration section in linkman:pkgctl-version[1].
 
 Options
 -------
@@ -36,8 +40,8 @@ Options
 *-h, --help*::
 	Show a help text
 
-Errors
-------
+Exit Codes
+----------
 
 On exit, return one of the following codes:
 
@@ -56,6 +60,7 @@ On exit, return one of the following codes:
 See Also
 --------
 
+linkman:pkgctl-version[1]
 linkman:nvchecker[1]
 
 include::include/footer.asciidoc[]
diff --git a/doc/man/pkgctl-version-upgrade.1.asciidoc b/doc/man/pkgctl-version-upgrade.1.asciidoc
index 7f9cd96..68e6369 100644
--- a/doc/man/pkgctl-version-upgrade.1.asciidoc
+++ b/doc/man/pkgctl-version-upgrade.1.asciidoc
@@ -3,7 +3,7 @@ pkgctl-version-upgrade(1)
 
 Name
 ----
-pkgctl-version-upgrade - Upgrade the PKGBUILD according to the latest available upstream version
+pkgctl-version-upgrade - Adjust the PKGBUILD to match the latest upstream version
 
 Synopsis
 --------
@@ -12,12 +12,25 @@ pkgctl version upgrade [OPTIONS] [PKGBASE...]
 Description
 -----------
 
-Upgrade the PKGBUILD according to the latest available upstream version.
+Streamlines the process of keeping PKGBUILD files up-to-date with the latest
+upstream versions.
 
-Uses nvchecker, a .nvchecker.toml file and the current PKGBUILD pkgver to check
-if there is a newer package version available.
+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.
 
-The current working directory is used if no PKGBASE is specified.
+Outputs a summary of upgraded packages, up-to-date packages, and any check
+failures.
+
+This simplifies the maintenance of PKGBUILD files, reducing the manual effort
+required to track and implement version changes from upstream sources.
+
+Configuration
+-------------
+
+Uses linkman:nvchecker[1] and a `.nvchecker.toml` file located alongside the
+PKGBUILD. Refer to the configuration section in linkman:pkgctl-version[1].
 
 Options
 -------
@@ -31,6 +44,7 @@ Options
 See Also
 --------
 
+linkman:pkgctl-version[1]
 linkman:nvchecker[1]
 
 include::include/footer.asciidoc[]
diff --git a/doc/man/pkgctl-version.1.asciidoc b/doc/man/pkgctl-version.1.asciidoc
index e71becd..fa83314 100644
--- a/doc/man/pkgctl-version.1.asciidoc
+++ b/doc/man/pkgctl-version.1.asciidoc
@@ -3,7 +3,7 @@ pkgctl-version(1)
 
 Name
 ----
-pkgctl-version - Package version related commands.
+pkgctl-version - Check and manage package versions against upstream
 
 
 Synopsis
@@ -13,7 +13,28 @@ pkgctl version [OPTIONS] [SUBCOMMAND]
 Description
 -----------
 
-Package version related commands such as checking if a package is out of date.
+Commands related to package versions, including checks for outdated packages.
+
+Uses linkman:nvchecker[1] and a `.nvchecker.toml` file located alongside the
+PKGBUILD.
+
+Configuration
+-------------
+
+The `.nvchecker.toml` file must contain a section that matches the
+package's pkgbase. The pkgbase section within the `.nvchecker.toml` file
+specifies the source and method for checking the latest version of the
+corresponding package.
+
+For detailed information on the various configuration options available for the
+`.nvchecker.toml` file, refer to the configuration files section in
+linkman:nvchecker[1]. This documentation provides insights into the possible
+options that can be utilized to customize the version checking process.
+
+To supply GitHub or GitLab tokens to nvchecker, a `keyfile.toml` should be
+placed in the `$XDG_CONFIG_HOME`/nvchecker` directory. This keyfile is
+used for providing the necessary authentication tokens required for
+accessing the GitHub or GitLab API.
 
 Options
 -------
@@ -25,10 +46,10 @@ Subcommands
 -----------
 
 pkgctl version check::
-	Check if there is an new upstream version available
+	Compares local package versions against upstream
 
 pkgctl version upgrade::
-	Upgrade the PKGBUILD according to the latest available upstream version
+	Adjust the PKGBUILD to match the latest upstream version
 
 See Also
 --------
diff --git a/doc/man/pkgctl.1.asciidoc b/doc/man/pkgctl.1.asciidoc
index 929ae9a..d9a1d8c 100644
--- a/doc/man/pkgctl.1.asciidoc
+++ b/doc/man/pkgctl.1.asciidoc
@@ -51,7 +51,7 @@ pkgctl search::
 	Search for an expression across the GitLab packaging group
 
 pkgctl version::
-	Package version related commands
+	Check and manage package versions against upstream
 
 See Also
 --------
diff --git a/src/lib/version.sh b/src/lib/version.sh
index 4340fca..ac810ae 100644
--- a/src/lib/version.sh
+++ b/src/lib/version.sh
@@ -15,11 +15,11 @@ pkgctl_version_usage() {
     cat <<- _EOF_
 		Usage: ${COMMAND} [COMMAND] [OPTIONS]
 
-		Package version related commands.
+		Check and manage package versions against upstream.
 
 		COMMANDS
-		    check      Check if there is a newer version availble
-		    upgrade    Upgrade the PKGBUILD according to the latest available upstream version
+		    check      Compares local package versions against upstream
+		    upgrade    Adjust the PKGBUILD to match the latest upstream version
 
 		OPTIONS
 		    -h, --help    Show this help text
diff --git a/src/lib/version/check.sh b/src/lib/version/check.sh
index cc8827d..006fd80 100644
--- a/src/lib/version/check.sh
+++ b/src/lib/version/check.sh
@@ -28,13 +28,15 @@ pkgctl_version_check_usage() {
 	cat <<- _EOF_
 		Usage: ${COMMAND} [OPTIONS] [PKGBASE]...
 
-		Uses nvchecker, a .nvchecker.toml file and the current PKGBUILD
-		pkgver to check if there is a newer package version available.
+		Compares the versions of packages in the local packaging repository against
+		their latest upstream versions.
 
-		The current working directory is used if no PKGBASE is specified.
+		Upon execution, it generates a grouped list that provides detailed insights
+		into each package's status. For each package, it displays the current local
+		version alongside the latest version available upstream.
 
-		To provide GitHub or GitLab tokens to nvchecker, a keyfile.toml
-		will be supplied from \${XDG_CONFIG_HOME}/nvchecker.
+		Outputs a summary of up-to-date packages, out-of-date packages, and any
+		check failures.
 
 		OPTIONS
 		    -v, --verbose    Display results including up-to-date versions
diff --git a/src/lib/version/upgrade.sh b/src/lib/version/upgrade.sh
index 70513ce..87744b0 100644
--- a/src/lib/version/upgrade.sh
+++ b/src/lib/version/upgrade.sh
@@ -24,12 +24,16 @@ pkgctl_version_upgrade_usage() {
 	cat <<- _EOF_
 		Usage: ${COMMAND} [OPTIONS] [PKGBASE]...
 
-		Upgrade the PKGBUILD according to the latest available upstream version
+		Streamlines the process of keeping PKGBUILD files up-to-date with the latest
+		upstream versions.
 
-		Uses nvchecker, a .nvchecker.toml file and the current PKGBUILD
-		pkgver to check if there is a newer package version available.
+		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.
 
-		The current working directory is used if no PKGBASE is specified.
+		Outputs a summary of upgraded packages, up-to-date packages, and any check
+		failures.
 
 		OPTIONS
 		    -v, --verbose    Display results including up-to-date versions
diff --git a/src/pkgctl.in b/src/pkgctl.in
index 9deb4b2..50c14b5 100644
--- a/src/pkgctl.in
+++ b/src/pkgctl.in
@@ -27,7 +27,7 @@ usage() {
 		    release Release step to commit, tag and upload build artifacts
 		    repo    Manage Git packaging repositories and their configuration
 		    search  Search for an expression across the GitLab packaging group
-		    version Package version related commands
+		    version Check and manage package versions against upstream
 
 		OPTIONS
 		    -h, --help     Show this help text
-- 
cgit v1.2.3-70-g09d2

