From f2cafa3cb0941be8235025434620adbf5849a432 Mon Sep 17 00:00:00 2001 From: Levente Polyak Date: Sun, 3 Dec 2023 19:46:09 +0100 Subject: feat(clone): speedup maintainer and universe clone query There is a single endpoint now to list all pkgbases and their current maintainers. Use this endpoint for speeding up the clone of all packages of a maintainer by only issuing a single API call. Component: pkgctl repo clone Signed-off-by: Levente Polyak --- src/lib/common.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'src/lib/common.sh') diff --git a/src/lib/common.sh b/src/lib/common.sh index 3d1ee56..7589120 100644 --- a/src/lib/common.sh +++ b/src/lib/common.sh @@ -28,6 +28,7 @@ export GIT_PACKAGING_NAMESPACE_ID=11323 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 # check if messages are to be printed using color if [[ -t 2 && "$TERM" != dumb ]] || [[ ${DEVTOOLS_COLOR} == always ]]; then @@ -53,6 +54,11 @@ stat_done() { printf "${BOLD}done${ALL_OFF}\n" >&2 } +stat_failed() { + # shellcheck disable=2059 + printf "${BOLD}${RED}failed${ALL_OFF}\n" >&2 +} + msg_success() { local msg=$1 local padding @@ -77,6 +83,15 @@ msg_warn() { printf "%s %s\n" "${padding}${YELLOW}!${ALL_OFF}" "${msg}" >&2 } +print_workdir_error() { + if [[ ! -f "${WORKDIR}"/error ]]; then + return + fi + while read -r LINE; do + error '%s' "${LINE}" + done < "${WORKDIR}/error" +} + _setup_workdir=false setup_workdir() { [[ -z ${WORKDIR:-} ]] && WORKDIR=$(mktemp -d --tmpdir "${0##*/}.XXXXXXXXXX") -- cgit v1.2.3-70-g09d2 From d00a2b989044fe452792e108a15b6c901a1a1d4c Mon Sep 17 00:00:00 2001 From: Levente Polyak Date: Sun, 11 Jun 2023 23:31:18 +0200 Subject: feat(configure): ship default ignore via exclude BREAKING CHANGE: Increments the repo spec version which requires to reconfigure all existing packaging repo clones. Fixes #129 Component: pkgctl repo configure Signed-off-by: Levente Polyak --- Makefile | 8 +++++++- config/git/template/hooks/applypatch-msg | 1 + config/git/template/hooks/commit-msg | 1 + config/git/template/hooks/post-applypatch | 1 + config/git/template/hooks/post-checkout | 1 + config/git/template/hooks/post-commit | 1 + config/git/template/hooks/post-merge | 1 + config/git/template/hooks/pre-applypatch | 1 + config/git/template/hooks/pre-commit | 1 + config/git/template/hooks/pre-merge-commit | 1 + config/git/template/hooks/pre-push | 1 + config/git/template/hooks/pre-rebase | 1 + config/git/template/hooks/prepare-commit-msg | 1 + config/git/template/info/exclude | 28 ++++++++++++++++++++++++++++ doc/man/pkgctl-repo-configure.1.asciidoc | 2 ++ src/commitpkg.in | 6 ++++++ src/lib/common.sh | 2 +- src/lib/repo/configure.sh | 25 ++++++++++++++++++++++++- 18 files changed, 80 insertions(+), 3 deletions(-) create mode 100755 config/git/template/hooks/applypatch-msg create mode 100755 config/git/template/hooks/commit-msg create mode 100755 config/git/template/hooks/post-applypatch create mode 100755 config/git/template/hooks/post-checkout create mode 100755 config/git/template/hooks/post-commit create mode 100755 config/git/template/hooks/post-merge create mode 100755 config/git/template/hooks/pre-applypatch create mode 100755 config/git/template/hooks/pre-commit create mode 100755 config/git/template/hooks/pre-merge-commit create mode 100755 config/git/template/hooks/pre-push create mode 100755 config/git/template/hooks/pre-rebase create mode 100755 config/git/template/hooks/prepare-commit-msg create mode 100644 config/git/template/info/exclude (limited to 'src/lib/common.sh') diff --git a/Makefile b/Makefile index 36f1b9b..afdb602 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ LIBRARY_SRC = $(call rwildcard,src/lib,*.sh) LIBRARY = $(addprefix $(BUILDDIR)/,$(patsubst src/%,%,$(patsubst %.in,%,$(LIBRARY_SRC)))) MAKEPKG_CONFIGS=$(wildcard config/makepkg/*) PACMAN_CONFIGS=$(wildcard config/pacman/*) +GIT_CONFIGS = $(wildcard config/git/*) SETARCH_ALIASES = $(wildcard config/setarch-aliases.d/*) MANS = $(addprefix $(BUILDDIR)/,$(patsubst %.asciidoc,%,$(wildcard doc/man/*.asciidoc))) @@ -97,9 +98,12 @@ $(BUILDDIR)/doc/man/%: doc/man/%.asciidoc doc/asciidoc.conf doc/man/include/foot @a2x --no-xmllint --asciidoc-opts="-f doc/asciidoc.conf" -d manpage -f manpage --destination-dir=$(BUILDDIR)/doc/man -a pkgdatadir=$(DATADIR) $< conf: - @install -d $(BUILDDIR)/makepkg.conf.d $(BUILDDIR)/pacman.conf.d + @install -d $(BUILDDIR)/makepkg.conf.d @cp -a $(MAKEPKG_CONFIGS) $(BUILDDIR)/makepkg.conf.d + @install -d $(BUILDDIR)/pacman.conf.d @cp -a $(PACMAN_CONFIGS) $(BUILDDIR)/pacman.conf.d + @install -d $(BUILDDIR)/git.conf.d + cp -a $(GIT_CONFIGS) $(BUILDDIR)/git.conf.d clean: rm -rf $(BUILDDIR) @@ -112,6 +116,7 @@ install: all install -m0755 ${BINPROGS} $(DESTDIR)$(PREFIX)/bin install -dm0755 $(DESTDIR)$(DATADIR)/lib cp -ra $(BUILDDIR)/lib/* $(DESTDIR)$(DATADIR)/lib + cp -a $(BUILDDIR)/git.conf.d -t $(DESTDIR)$(DATADIR) for conf in $(notdir $(MAKEPKG_CONFIGS)); do install -Dm0644 $(BUILDDIR)/makepkg.conf.d/$$conf $(DESTDIR)$(DATADIR)/makepkg.conf.d/$${conf##*/}; done for conf in $(notdir $(PACMAN_CONFIGS)); do install -Dm0644 $(BUILDDIR)/pacman.conf.d/$$conf $(DESTDIR)$(DATADIR)/pacman.conf.d/$${conf##*/}; done for a in ${SETARCH_ALIASES}; do install -m0644 $$a -t $(DESTDIR)$(DATADIR)/setarch-aliases.d; done @@ -129,6 +134,7 @@ uninstall: for f in $(notdir $(BINPROGS)); do rm -f $(DESTDIR)$(PREFIX)/bin/$$f; done for f in $(notdir $(LIBRARY)); do rm -f $(DESTDIR)$(DATADIR)/lib/$$f; done rm -rf $(DESTDIR)$(DATADIR)/lib + rm -rf $(DESTDIR)$(DATADIR)/git.conf.d for conf in $(notdir $(MAKEPKG_CONFIGS)); do rm -f $(DESTDIR)$(DATADIR)/makepkg.conf.d/$${conf##*/}; done for conf in $(notdir $(PACMAN_CONFIGS)); do rm -f $(DESTDIR)$(DATADIR)/pacman.conf.d/$${conf##*/}; done for f in $(notdir $(SETARCH_ALIASES)); do rm -f $(DESTDIR)$(DATADIR)/setarch-aliases.d/$$f; done diff --git a/config/git/template/hooks/applypatch-msg b/config/git/template/hooks/applypatch-msg new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/config/git/template/hooks/applypatch-msg @@ -0,0 +1 @@ +#!/bin/sh diff --git a/config/git/template/hooks/commit-msg b/config/git/template/hooks/commit-msg new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/config/git/template/hooks/commit-msg @@ -0,0 +1 @@ +#!/bin/sh diff --git a/config/git/template/hooks/post-applypatch b/config/git/template/hooks/post-applypatch new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/config/git/template/hooks/post-applypatch @@ -0,0 +1 @@ +#!/bin/sh diff --git a/config/git/template/hooks/post-checkout b/config/git/template/hooks/post-checkout new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/config/git/template/hooks/post-checkout @@ -0,0 +1 @@ +#!/bin/sh diff --git a/config/git/template/hooks/post-commit b/config/git/template/hooks/post-commit new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/config/git/template/hooks/post-commit @@ -0,0 +1 @@ +#!/bin/sh diff --git a/config/git/template/hooks/post-merge b/config/git/template/hooks/post-merge new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/config/git/template/hooks/post-merge @@ -0,0 +1 @@ +#!/bin/sh diff --git a/config/git/template/hooks/pre-applypatch b/config/git/template/hooks/pre-applypatch new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/config/git/template/hooks/pre-applypatch @@ -0,0 +1 @@ +#!/bin/sh diff --git a/config/git/template/hooks/pre-commit b/config/git/template/hooks/pre-commit new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/config/git/template/hooks/pre-commit @@ -0,0 +1 @@ +#!/bin/sh diff --git a/config/git/template/hooks/pre-merge-commit b/config/git/template/hooks/pre-merge-commit new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/config/git/template/hooks/pre-merge-commit @@ -0,0 +1 @@ +#!/bin/sh diff --git a/config/git/template/hooks/pre-push b/config/git/template/hooks/pre-push new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/config/git/template/hooks/pre-push @@ -0,0 +1 @@ +#!/bin/sh diff --git a/config/git/template/hooks/pre-rebase b/config/git/template/hooks/pre-rebase new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/config/git/template/hooks/pre-rebase @@ -0,0 +1 @@ +#!/bin/sh diff --git a/config/git/template/hooks/prepare-commit-msg b/config/git/template/hooks/prepare-commit-msg new file mode 100755 index 0000000..1a24852 --- /dev/null +++ b/config/git/template/hooks/prepare-commit-msg @@ -0,0 +1 @@ +#!/bin/sh diff --git a/config/git/template/info/exclude b/config/git/template/info/exclude new file mode 100644 index 0000000..ba46529 --- /dev/null +++ b/config/git/template/info/exclude @@ -0,0 +1,28 @@ +/pkg +/src +/*/ +!/keys/ + +/*.log +/*.tar.* +/*.tar +/*.tgz +/*.zst +/*.gz +/*.xz +/*.bz2 +/*.zip +/*.xpi +/*.jar +/*.whl +/*.war +/*.deb +/*.ttf +/*.dat +/*.iso +/*.asc +/*.sig +/*.signature +/*.sign +/*.SHA256SUMS +/*.sha256 diff --git a/doc/man/pkgctl-repo-configure.1.asciidoc b/doc/man/pkgctl-repo-configure.1.asciidoc index 6bdea93..12d879c 100644 --- a/doc/man/pkgctl-repo-configure.1.asciidoc +++ b/doc/man/pkgctl-repo-configure.1.asciidoc @@ -22,6 +22,8 @@ The remote protocol is automatically determined from the author email address by choosing SSH for all official packager identities and read-only HTTPS otherwise. +Git default excludes and hooks are applied to the configured repo. + Options ------- diff --git a/src/commitpkg.in b/src/commitpkg.in index 6d4b014..016ab22 100644 --- a/src/commitpkg.in +++ b/src/commitpkg.in @@ -71,6 +71,12 @@ if ! repo_spec=$(git config --local devtools.version) || [[ ${repo_spec} != "${G exit 1 fi +if ! repo_variant=$(git config --local devtools.variant) || [[ ${repo_variant} != canonical ]]; then + error "cannot release from a repository with none canonical specs (%s), try:" "${repo_variant:-development}" + msg2 'pkgctl repo configure' + exit 1 +fi + if [[ "$(git symbolic-ref --short HEAD)" != main ]]; then die 'must be run from the main branch' fi diff --git a/src/lib/common.sh b/src/lib/common.sh index 7589120..82fdd2e 100644 --- a/src/lib/common.sh +++ b/src/lib/common.sh @@ -22,7 +22,7 @@ export BUILDTOOLVER=@buildtoolver@ # Set common properties export PACMAN_KEYRING_DIR=/etc/pacman.d/gnupg export GITLAB_HOST=gitlab.archlinux.org -export GIT_REPO_SPEC_VERSION=1 +export GIT_REPO_SPEC_VERSION=2 export GIT_PACKAGING_NAMESPACE=archlinux/packaging/packages export GIT_PACKAGING_NAMESPACE_ID=11323 export GIT_PACKAGING_URL_SSH="git@${GITLAB_HOST}:${GIT_PACKAGING_NAMESPACE}" diff --git a/src/lib/repo/configure.sh b/src/lib/repo/configure.sh index 1ddb1b8..b3c188c 100644 --- a/src/lib/repo/configure.sh +++ b/src/lib/repo/configure.sh @@ -17,6 +17,7 @@ source /usr/share/makepkg/util/config.sh source /usr/share/makepkg/util/message.sh set -e +shopt -s nullglob pkgctl_repo_configure_usage() { @@ -34,6 +35,8 @@ pkgctl_repo_configure_usage() { address by choosing SSH for all official packager identities and read-only HTTPS otherwise. + Git default excludes and hooks are applied to the configured repo. + OPTIONS --protocol https Configure remote url to use https -j, --jobs N Run up to N jobs in parallel (default: $(nproc)) @@ -104,7 +107,7 @@ pkgctl_repo_configure() { # variables local -r command=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}} - local path realpath pkgbase remote_url project_path + local path realpath pkgbase remote_url project_path hook local PACKAGER GPGKEY packager_name packager_email while (( $# )); do @@ -230,7 +233,15 @@ pkgctl_repo_configure() { git config branch.main.merge refs/heads/main fi + # configure spec version and variant to avoid using development hooks in production git config devtools.version "${GIT_REPO_SPEC_VERSION}" + if [[ ${_DEVTOOLS_LIBRARY_DIR} == /usr/share/devtools ]]; then + git config devtools.variant canonical + else + warning "Configuring with development version of pkgctl, do not use this repo in production" + git config devtools.variant development + fi + git config pull.rebase true git config branch.autoSetupRebase always git config branch.main.remote origin @@ -257,6 +268,18 @@ pkgctl_repo_configure() { git config user.signingKey "${GPGKEY}" fi + # set default git exclude + mkdir -p .git/info + ln -sf "${_DEVTOOLS_LIBRARY_DIR}/git.conf.d/template/info/exclude" \ + .git/info/exclude + + # set default git hooks + mkdir -p .git/hooks + rm -f .git/hooks/*.sample + for hook in "${_DEVTOOLS_LIBRARY_DIR}"/git.conf.d/template/hooks/*; do + ln -sf "${hook}" ".git/hooks/$(basename "${hook}")" + done + if ! git ls-remote origin &>/dev/null; then warning "configured remote origin may not exist, run:" msg2 "pkgctl repo create ${pkgbase}" -- cgit v1.2.3-70-g09d2 From 0e538cf4983f27548077c0a2a272ecd8deebacb1 Mon Sep 17 00:00:00 2001 From: Levente Polyak Date: Fri, 5 Jan 2024 13:38:19 +0100 Subject: fix(util): open lock file in append mode to avoid overwriting files Previously if we opened a lock on an existing file with useful content we overwrote it with an empty file. This is obviously not desired, hence open the file handle in append mode preserving its content. Signed-off-by: Levente Polyak --- src/lib/common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib/common.sh') diff --git a/src/lib/common.sh b/src/lib/common.sh index 82fdd2e..29b0343 100644 --- a/src/lib/common.sh +++ b/src/lib/common.sh @@ -135,7 +135,7 @@ lock() { # Only reopen the FD if it wasn't handed to us if ! [[ "/dev/fd/$1" -ef "$2" ]]; then mkdir -p -- "$(dirname -- "$2")" - eval "exec $1>"'"$2"' + eval "exec $1>>"'"$2"' fi if ! flock -n "$1"; then -- cgit v1.2.3-70-g09d2 From d0dc0e1a32d6ed9b166eca777f7fb7071c4c2df1 Mon Sep 17 00:00:00 2001 From: Levente Polyak Date: Fri, 5 Jan 2024 19:23:52 +0100 Subject: feat(search): add optional plain output formatting This allows to run the search command without bats, which is not used in the default pretty output format. Component: pkgctl search Signed-off-by: Levente Polyak --- README.md | 5 +- contrib/completion/bash/devtools.in | 11 ++++- contrib/completion/zsh/_devtools.in | 6 ++- doc/man/pkgctl-search.1.asciidoc | 23 +++++++-- src/lib/common.sh | 4 +- src/lib/search.sh | 93 +++++++++++++++++++++++++++++++++---- src/lib/valid-search.sh | 11 +++++ 7 files changed, 134 insertions(+), 19 deletions(-) create mode 100644 src/lib/valid-search.sh (limited to 'src/lib/common.sh') diff --git a/README.md b/README.md index 6c36a37..ca761db 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,6 @@ Component: pkgctl db remove - arch-install-scripts - awk - bash -- bats - binutils - coreutils - diffutils @@ -87,6 +86,10 @@ Component: pkgctl db remove - mercurial - subversion +### Optional Dependencies + +- bats (pretty printing) + ### Development Dependencies - asciidoc diff --git a/contrib/completion/bash/devtools.in b/contrib/completion/bash/devtools.in index 155bb7e..4c7b73a 100644 --- a/contrib/completion/bash/devtools.in +++ b/contrib/completion/bash/devtools.in @@ -9,6 +9,8 @@ source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-tags.sh source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-repos.sh # shellcheck source=src/lib/valid-inspect.sh source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-inspect.sh +# shellcheck source=src/lib/valid-search.sh +source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-search.sh _binary_arch=${DEVTOOLS_VALID_ARCHES[*]:0:-1} _colors=(never always auto) @@ -333,11 +335,15 @@ _pkgctl_repo_web_opts() { _filedir -d; } _pkgctl_search_args=( - --json --no-default-filter + --json + -F --format + -N --no-line-number -h --help ) _pkgctl_search_opts() { :; } +_pkgctl_search_args__format_opts() { _devtools_completions_search_format; } +_pkgctl_search_args_F_opts() { _devtools_completions_search_format; } _pkgctl_diff_args=( @@ -391,6 +397,9 @@ _devtools_completions_protocol() { _devtools_completions_inspect() { mapfile -t COMPREPLY < <(compgen -W "${DEVTOOLS_VALID_INSPECT_MODES[*]}" -- "$cur") } +_devtools_completions_search_format() { + mapfile -t COMPREPLY < <(compgen -W "${valid_search_output_format[*]}" -- "$cur") +} __devtools_complete() { local service=$1 diff --git a/contrib/completion/zsh/_devtools.in b/contrib/completion/zsh/_devtools.in index 120b47a..448fbed 100644 --- a/contrib/completion/zsh/_devtools.in +++ b/contrib/completion/zsh/_devtools.in @@ -9,6 +9,8 @@ source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-tags.sh source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-repos.sh # shellcheck source=src/lib/valid-inspect.sh source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-inspect.sh +# shellcheck source=src/lib/valid-search.sh +source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-search.sh _binary_arch=${DEVTOOLS_VALID_ARCHES[*]:0:-1} _colors=(never always auto) @@ -140,8 +142,10 @@ _pkgctl_repo_web_args=( ) _pkgctl_search_args=( - '--json[Enable printing results in JSON]' '--no-default-filter[Do not apply default filter (like -path:keys/pgp/*.asc)]' + '--json[Enable printing results in JSON]' + '(-F --format)'{-F,--format}"[Controls the formatting of the results]:format:($valid_search_output_format[*])" + '(-N --no-line-number)'{-N,--no-line-number}"[Don't show line numbers when formatting results]" '(-h --help)'{-h,--help}'[Display usage]' '1:query' ) diff --git a/doc/man/pkgctl-search.1.asciidoc b/doc/man/pkgctl-search.1.asciidoc index fb79b88..8172b00 100644 --- a/doc/man/pkgctl-search.1.asciidoc +++ b/doc/man/pkgctl-search.1.asciidoc @@ -20,7 +20,7 @@ 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 +`'pkgctl auth'` command to authenticate with GitLab or view the authentication status. Search Tips @@ -41,14 +41,27 @@ Search Tips Options ------- -*--json*:: - Enable printing results in JSON +*-h, --help*:: + Show a help text + +Filter Options +-------------- *--no-default-filter*:: Do not apply default filter (like -path:keys/pgp/*.asc) -*-h, --help*:: - Show a help text +Output Options +-------------- + +*--json*:: + Enable printing in JSON; Shorthand for `'--format json'` + +*-F, --format* 'FORMAT':: + Controls the formatting of the results; `FORMAT` is `'pretty'`, `'plain'`, + or `'json'` (default `pretty`) + +*-N, --no-line-number*:: + Don't show line numbers when formatting results See Also -------- diff --git a/src/lib/common.sh b/src/lib/common.sh index 29b0343..17b91bc 100644 --- a/src/lib/common.sh +++ b/src/lib/common.sh @@ -33,9 +33,11 @@ export PKGBASE_MAINTAINER_URL=https://archlinux.org/packages/pkgbase-maintainer # check if messages are to be printed using color if [[ -t 2 && "$TERM" != dumb ]] || [[ ${DEVTOOLS_COLOR} == always ]]; then colorize + PURPLE="$(tput setaf 5)" + DARK_GREEN="$(tput setaf 2)" else # shellcheck disable=2034 - declare -gr ALL_OFF='' BOLD='' BLUE='' GREEN='' RED='' YELLOW='' + declare -gr ALL_OFF='' BOLD='' BLUE='' GREEN='' RED='' YELLOW='' PURPLE='' fi stat_busy() { diff --git a/src/lib/search.sh b/src/lib/search.sh index cf64db3..862af25 100644 --- a/src/lib/search.sh +++ b/src/lib/search.sh @@ -12,7 +12,10 @@ source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh source "${_DEVTOOLS_LIBRARY_DIR}"/lib/cache.sh # shellcheck source=src/lib/api/gitlab.sh source "${_DEVTOOLS_LIBRARY_DIR}"/lib/api/gitlab.sh +# shellcheck source=src/lib/valid-search.sh +source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-search.sh +source /usr/share/makepkg/util/util.sh source /usr/share/makepkg/util/message.sh set -eo pipefail @@ -48,16 +51,33 @@ pkgctl_search_usage() { ! 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 + -h, --help Show this help text + + FILTER OPTIONS + --no-default-filter Do not apply default filter (like -path:keys/pgp/*.asc) + + OUTPUT OPTIONS + --json Enable printing in JSON; Shorthand for '--format json' + -F, --format FORMAT Controls the formatting of the results; FORMAT is 'pretty', + 'plain', or 'json' (default: pretty) + -N, --no-line-number Don't show line numbers when formatting results EXAMPLES $ ${COMMAND} linux - $ ${COMMAND} '"pytest -v" +PYTHONPATH' + $ ${COMMAND} --json '"pytest -v" +PYTHONPATH' _EOF_ } +pkgctl_search_check_option_group_format() { + local option=$1 + local output_format=$2 + if [[ -n ${output_format} ]]; then + die "The argument '%s' cannot be used with one or more of the other specified arguments" "${option}" + exit 1 + fi + return 0 +} + pkgctl_search() { if (( $# < 1 )); then pkgctl_search_usage @@ -66,15 +86,17 @@ pkgctl_search() { # options local search - local formatter=pretty + local output_format= local use_default_filter=1 + local line_numbers=1 # variables + local bats_style="header,grid" 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 + local mapping_output path startline currentline data line while (( $# )); do case $1 in @@ -82,12 +104,26 @@ pkgctl_search() { pkgctl_search_usage exit 0 ;; + --no-default-filter) + use_default_filter=0 + shift + ;; --json) - formatter=json + pkgctl_search_check_option_group_format "$1" "${output_format}" + output_format=json shift ;; - --no-default-filter) - use_default_filter=0 + -F|--format) + (( $# <= 1 )) && die "missing argument for %s" "$1" + pkgctl_search_check_option_group_format "$1" "${output_format}" + output_format="${2}" + if ! in_array "${output_format}" "${valid_search_output_format[@]}"; then + die "Unknown output format: %s" "${output_format}" + fi + shift 2 + ;; + -N|--no-line-number) + line_numbers=0 shift ;; --) @@ -114,16 +150,35 @@ pkgctl_search() { search+=" ${default_filter}" fi + # assign default output format + if [[ -z ${output_format} ]]; then + output_format=pretty + fi + + # check for optional dependencies + if [[ ${output_format} == pretty ]] && ! command -v bats &>/dev/null; then + warning "Failed to find optional dependency 'bats': falling back to plain output" + output_format=plain + fi + + # populate line numbers option + if (( line_numbers )); then + bats_style="numbers,${bats_style}" + fi + + # call the gitlab search API stat_busy "Querying gitlab search api" output=$(gitlab_api_search "${search}") stat_done + # collect project ids whose name needs to be looked up 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}" )) + # look up project names stat_busy "Querying project names" local entries="${#project_ids[@]}" local until=0 @@ -171,7 +226,7 @@ pkgctl_search() { lock_close 11 # output mode JSON - if [[ ${formatter} == json ]]; then + if [[ ${output_format} == json ]]; then jq --from-file <( for project_id in $(jq '.[].project_id' <<< "${output}"); do project_name=${project_name_lookup[${project_id}]} @@ -197,6 +252,23 @@ pkgctl_search() { unset "data[${#data[@]}-1]" fi + # output mode plain + if [[ ${output_format} == plain ]]; then + printf "%s%s%s\n" "${PURPLE}" "${project_name}/${path}" "${ALL_OFF}" + + currentline=${startline} + for line in "${data[@]}"; do + if (( line_numbers )); then + line="${DARK_GREEN}${currentline}${ALL_OFF}: ${line}" + currentline=$(( currentline + 1 )) + fi + printf "%s\n" "${line}" + done + printf "\n" + + continue + fi + # prepend empty lines to match startline if (( startline > 1 )); then mapfile -t data < <( @@ -210,6 +282,7 @@ pkgctl_search() { --line-range "${startline}:" \ --paging=never \ --force-colorization \ + --style "${bats_style}" \ --map-syntax "PKGBUILD:Bourne Again Shell (bash)" \ --map-syntax ".SRCINFO:INI" \ --map-syntax "*install:Bourne Again Shell (bash)" \ diff --git a/src/lib/valid-search.sh b/src/lib/valid-search.sh new file mode 100644 index 0000000..43799a5 --- /dev/null +++ b/src/lib/valid-search.sh @@ -0,0 +1,11 @@ +#!/hint/bash +# +# SPDX-License-Identifier: GPL-3.0-or-later +: + +# shellcheck disable=2034 +valid_search_output_format=( + pretty + plain + json +) -- cgit v1.2.3-70-g09d2 From 2b8033b91132d303603f370a54eef02949703750 Mon Sep 17 00:00:00 2001 From: Christian Heusel 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 Signed-off-by: Christian Heusel --- 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 'src/lib/common.sh') 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 66e83c950cfa1c51820f04130abfacaf7c6b4c4c Mon Sep 17 00:00:00 2001 From: Levente Polyak Date: Thu, 18 Jan 2024 02:29:27 +0100 Subject: feat(version): pretty print and group together version check results Collect all check results in arrays and pretty print the results after grouping them together based on out-of-date, up-to-date and failures. Print a summary that shows a brief statistic about the results when processing multiple check items. Component: pkgctl version check Component: pkgctl version upgrade Signed-off-by: Levente Polyak --- src/lib/common.sh | 3 +- src/lib/version/check.sh | 72 +++++++++++++++++++++++++++++++++++++--- src/lib/version/upgrade.sh | 82 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 145 insertions(+), 12 deletions(-) (limited to 'src/lib/common.sh') diff --git a/src/lib/common.sh b/src/lib/common.sh index a93e906..9d5622e 100644 --- a/src/lib/common.sh +++ b/src/lib/common.sh @@ -36,9 +36,10 @@ if [[ -t 2 && "$TERM" != dumb ]] || [[ ${DEVTOOLS_COLOR} == always ]]; then colorize PURPLE="$(tput setaf 5)" DARK_GREEN="$(tput setaf 2)" + UNDERLINE="$(tput smul)" else # shellcheck disable=2034 - declare -gr ALL_OFF='' BOLD='' BLUE='' GREEN='' RED='' YELLOW='' PURPLE='' + declare -gr ALL_OFF='' BOLD='' BLUE='' GREEN='' RED='' YELLOW='' PURPLE='' DARK_GREEN='' UNDERLINE='' fi stat_busy() { diff --git a/src/lib/version/check.sh b/src/lib/version/check.sh index 600a346..ddd400d 100644 --- a/src/lib/version/check.sh +++ b/src/lib/version/check.sh @@ -39,6 +39,11 @@ pkgctl_version_check() { local pkgbases=() local path pkgbase upstream_version result + local up_to_date=() + local out_of_date=() + local failure=() + local section_separator='' + while (( $# )); do case $1 in -h|--help) @@ -87,7 +92,8 @@ pkgctl_version_check() { pkgbase=${pkgbase:-$pkgname} if ! result=$(get_upstream_version); then - msg_error "${pkgbase}: ${result}" + result="${BOLD}${pkgbase}${ALL_OFF}: ${result}" + failure+=("${result}") popd >/dev/null continue fi @@ -95,18 +101,49 @@ pkgctl_version_check() { if ! result=$(vercmp "${upstream_version}" "${pkgver}"); then result="${BOLD}${pkgbase}${ALL_OFF}: failed to compare version ${upstream_version} against ${pkgver}" - msg_error "${result}" - + failure+=("${result}") popd >/dev/null continue fi - if (( result > 0 )); then - msg2 "New ${pkgbase} version ${upstream_version} is available upstream" + if (( result == 0 )); then + result="${BOLD}${pkgbase}${ALL_OFF}: current version ${PURPLE}${pkgver}${ALL_OFF} is latest" + up_to_date+=("${result}") + elif (( result < 0 )); then + result="${BOLD}${pkgbase}${ALL_OFF}: current version ${PURPLE}${pkgver}${ALL_OFF} is never than ${DARK_GREEN}${upstream_version}${ALL_OFF}" + up_to_date+=("${result}") + elif (( result > 0 )); then + result="${BOLD}${pkgbase}${ALL_OFF}: upgrade from version ${PURPLE}${pkgver}${ALL_OFF} to ${DARK_GREEN}${upstream_version}${ALL_OFF}" + out_of_date+=("${result}") fi popd >/dev/null done + + if (( ${#failure[@]} > 0 )); then + printf "%sFailure%s\n" "${section_separator}${BOLD}${UNDERLINE}" "${ALL_OFF}" + section_separator=$'\n' + for result in "${failure[@]}"; do + msg_error " ${result}" + done + fi + + if (( ${#out_of_date[@]} > 0 )); then + printf "%sOut-of-date%s\n" "${section_separator}${BOLD}${UNDERLINE}" "${ALL_OFF}" + section_separator=$'\n' + for result in "${out_of_date[@]}"; do + msg_warn " ${result}" + done + fi + + # Show summary when processing multiple packages + if (( ${#pkgbases[@]} > 1 )); then + printf '%s' "${section_separator}" + pkgctl_version_check_summary \ + "${#up_to_date[@]}" \ + "${#out_of_date[@]}" \ + "${#failure[@]}" + fi } get_upstream_version() { @@ -194,3 +231,28 @@ nvchecker_check_error() { printf "%s\n" "${errors[@]}" return 1 } + +pkgctl_version_check_summary() { + local up_to_date_count=$1 + local out_of_date_count=$2 + local failure_count=$3 + + # print nothing if all stats are zero + if (( up_to_date_count == 0 )) && \ + (( out_of_date_count == 0 )) && \ + (( failure_count == 0 )); then + return 0 + fi + + # print summary for all none zero stats + printf "%sSummary%s\n" "${BOLD}${UNDERLINE}" "${ALL_OFF}" + if (( up_to_date_count > 0 )); then + msg_success " Up-to-date: ${BOLD}${up_to_date_count}${ALL_OFF}" 2>&1 + fi + if (( failure_count > 0 )); then + msg_error " Failure: ${BOLD}${failure_count}${ALL_OFF}" 2>&1 + fi + if (( out_of_date_count > 0 )); then + msg_warn " Out-of-date: ${BOLD}${out_of_date_count}${ALL_OFF}" 2>&1 + fi +} diff --git a/src/lib/version/upgrade.sh b/src/lib/version/upgrade.sh index 704431a..26a5ccb 100644 --- a/src/lib/version/upgrade.sh +++ b/src/lib/version/upgrade.sh @@ -40,6 +40,7 @@ _EOF_ pkgctl_version_upgrade() { local path upstream_version result local pkgbases=() + local exit_code=0 while (( $# )); do case $1 in @@ -88,21 +89,90 @@ pkgctl_version_upgrade() { . ./PKGBUILD pkgbase=${pkgbase:-$pkgname} - if ! upstream_version=$(get_upstream_version); then - die "Failed to get latest upstream version for %s" "${pkgbase}" + if ! result=$(get_upstream_version); then + result="${BOLD}${pkgbase}${ALL_OFF}: ${result}" + failure+=("${result}") + popd >/dev/null + continue fi + upstream_version=${result} if ! result=$(vercmp "${upstream_version}" "${pkgver}"); then - die "Failed to compare version %s against %s" "${upstream_version}" "${pkgver}" + result="${BOLD}${pkgbase}${ALL_OFF}: failed to compare version ${upstream_version} against ${pkgver}" + failure+=("${result}") + popd >/dev/null + continue fi - if (( result > 0 )); then - msg_success "${BOLD}${pkgbase}${ALL_OFF}: upgrading from version ${PURPLE}${pkgver}${ALL_OFF} to ${DARK_GREEN}${upstream_version}${ALL_OFF}" - + if (( result == 0 )); then + result="${BOLD}${pkgbase}${ALL_OFF}: current version ${PURPLE}${pkgver}${ALL_OFF} is latest" + up_to_date+=("${result}") + elif (( result < 0 )); then + result="${BOLD}${pkgbase}${ALL_OFF}: current version ${PURPLE}${pkgver}${ALL_OFF} is never than ${DARK_GREEN}${upstream_version}${ALL_OFF}" + up_to_date+=("${result}") + elif (( result > 0 )); then + result="${BOLD}${pkgbase}${ALL_OFF}: upgraded from version ${PURPLE}${pkgver}${ALL_OFF} to ${DARK_GREEN}${upstream_version}${ALL_OFF}" + out_of_date+=("${result}") + + # change the PKGBUILD pkgbuild_set_pkgver "${upstream_version}" pkgbuild_set_pkgrel 1 fi popd >/dev/null done + + if (( ${#failure[@]} > 0 )); then + exit_code=1 + printf "%sFailure%s\n" "${section_separator}${BOLD}${UNDERLINE}" "${ALL_OFF}" + section_separator=$'\n' + for result in "${failure[@]}"; do + msg_error " ${result}" + done + fi + + if (( ${#out_of_date[@]} > 0 )); then + printf "%sUpgraded%s\n" "${section_separator}${BOLD}${UNDERLINE}" "${ALL_OFF}" + section_separator=$'\n' + for result in "${out_of_date[@]}"; do + msg_warn " ${result}" + done + fi + + # Show summary when processing multiple packages + if (( ${#pkgbases[@]} > 1 )); then + printf '%s' "${section_separator}" + pkgctl_version_upgrade_summary \ + "${#up_to_date[@]}" \ + "${#out_of_date[@]}" \ + "${#failure[@]}" + fi + + # return status based on results + return "${exit_code}" +} + +pkgctl_version_upgrade_summary() { + local up_to_date_count=$1 + local out_of_date_count=$2 + local failure_count=$3 + + # print nothing if all stats are zero + if (( up_to_date_count == 0 )) && \ + (( out_of_date_count == 0 )) && \ + (( failure_count == 0 )); then + return 0 + fi + + # print summary for all none zero stats + printf "%sSummary%s\n" "${BOLD}${UNDERLINE}" "${ALL_OFF}" + if (( up_to_date_count > 0 )); then + msg_success " Up-to-date: ${BOLD}${up_to_date_count}${ALL_OFF}" 2>&1 + fi + if (( failure_count > 0 )); then + msg_error " Failure: ${BOLD}${failure_count}${ALL_OFF}" 2>&1 + fi + if (( out_of_date_count > 0 )); then + msg_warn " Upgraded: ${BOLD}${out_of_date_count}${ALL_OFF}" 2>&1 + fi } -- cgit v1.2.3-70-g09d2 From fedfc80ca15a196d565b9f5dc5159be594f74da3 Mon Sep 17 00:00:00 2001 From: Levente Polyak Date: Thu, 18 Jan 2024 02:39:34 +0100 Subject: feat(term): add terminal utils to handle a dynamic spinner The spinner uses a status file that can be used to dynamically update the message. The spinner itself buffers the output in a frame buffer variable before flushing a frame in one go. Signed-off-by: Levente Polyak --- src/lib/common.sh | 3 +- src/lib/util/term.sh | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 src/lib/util/term.sh (limited to 'src/lib/common.sh') diff --git a/src/lib/common.sh b/src/lib/common.sh index 9d5622e..63f43f1 100644 --- a/src/lib/common.sh +++ b/src/lib/common.sh @@ -13,7 +13,7 @@ set +u +o posix $DEVTOOLS_INCLUDE_COMMON_SH # Avoid any encoding problems -export LANG=C +export LANG=C.UTF-8 # Set buildtool properties export BUILDTOOL=devtools @@ -108,6 +108,7 @@ cleanup() { if [[ -n ${WORKDIR:-} ]] && $_setup_workdir; then rm -rf "$WORKDIR" fi + tput cnorm >&2 exit "${1:-0}" } 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 +} -- cgit v1.2.3-70-g09d2 From e0a84aefc35a4716fa91c13ec1c3909b50fccd5c Mon Sep 17 00:00:00 2001 From: Christian Heusel Date: Sat, 10 Feb 2024 20:14:16 +0100 Subject: fix(common): check before using tput with a fallback for colors The latest release of devtools has included some pretty printing capabilities and fancy terminal stuff with the spinner and so on. It seems like the existing safeguards to disable this for incapable terminals were not enough though, therefore we saw two types of errors: - offload-build: ``` ==> Building in chroot for [extra] (x86_64)... tput: unknown terminal "unknown" tput: unknown terminal "unknown" tput: unknown terminal "unknown" ``` - repro builders: ``` ==> Successfully switched to version tput: No value for $TERM and no -T specified ERROR: Failed checkout ``` The recently included fail option made this error populate to the command level and therefore increased its impact from a not so nice logging message to a more severe problem which made the command abort. We fix this by checking if tput is supported or else use the raw escape sequences instead of tput commands. Fixes: fedfc80 ("feat(term): add terminal utils to handle a dynamic spinner") Fixes: 66e83c9 ("feat(version): pretty print and group together version check results") Fixes: d0dc0e1 ("feat(search): add optional plain output formatting") Signed-off-by: Christian Heusel --- src/lib/common.sh | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'src/lib/common.sh') diff --git a/src/lib/common.sh b/src/lib/common.sh index 63f43f1..2d2d16f 100644 --- a/src/lib/common.sh +++ b/src/lib/common.sh @@ -34,9 +34,15 @@ 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 colorize - PURPLE="$(tput setaf 5)" - DARK_GREEN="$(tput setaf 2)" - UNDERLINE="$(tput smul)" + if tput setaf 0 &>/dev/null; then + PURPLE="$(tput setaf 5)" + DARK_GREEN="$(tput setaf 2)" + UNDERLINE="$(tput smul)" + else + PURPLE="\e[35m" + DARK_GREEN="\e[32m" + UNDERLINE="\e[4m" + fi else # shellcheck disable=2034 declare -gr ALL_OFF='' BOLD='' BLUE='' GREEN='' RED='' YELLOW='' PURPLE='' DARK_GREEN='' UNDERLINE='' @@ -108,7 +114,9 @@ cleanup() { if [[ -n ${WORKDIR:-} ]] && $_setup_workdir; then rm -rf "$WORKDIR" fi - tput cnorm >&2 + if tput setaf 0 &>/dev/null; then + tput cnorm >&2 + fi exit "${1:-0}" } -- cgit v1.2.3-70-g09d2 From 66a4357f3e001d02bbbb45fb247efaa2f505726d Mon Sep 17 00:00:00 2001 From: Levente Polyak Date: Wed, 14 Feb 2024 01:18:40 +0100 Subject: fix(common): ensure TERM is always set with a fallback to dumb This avoids some corner cases that some applications behave ill when TERM is completely unset. Instead, ensure we set TERM to dumb as a fallback, which should serve better than not having any term defined. Signed-off-by: Levente Polyak --- src/lib/common.sh | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/lib/common.sh') diff --git a/src/lib/common.sh b/src/lib/common.sh index 2d2d16f..ff767c6 100644 --- a/src/lib/common.sh +++ b/src/lib/common.sh @@ -31,6 +31,9 @@ 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 +# ensure TERM is set with a fallback to dumb +export TERM=${TERM:-dumb} + # check if messages are to be printed using color if [[ -t 2 && "$TERM" != dumb ]] || [[ ${DEVTOOLS_COLOR} == always ]]; then colorize -- cgit v1.2.3-70-g09d2