diff --git a/NEWS.rst b/NEWS.rst index c2291b0..053ad97 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -2,6 +2,16 @@ Release Notes ============= +iwdevtools-next (xxxx-xx-xx) +============================= + +New +--- +- workdir-cd: new tool to jump to the packages workdir in PORTAGE_TMPDIR and + then run a command in that directory such as listing its contents. (can + search for a partial atom, and use tab completion with bash/fish/zsh after + setting up shell integration) + iwdevtools-0.12.7 (2023-08-04) ============================== diff --git a/README.rst b/README.rst index 896570b..61ebc5b 100644 --- a/README.rst +++ b/README.rst @@ -9,6 +9,7 @@ Overview * `qa-openrc`_ tries to find a few common mistakes in OpenRC init scripts + `repo-cd`_ facilitates navigating Gentoo repos' directories with some perks ++ `workdir-cd`_ facilitates navigating Portage temp' directories with some perks + `eoldnew`_ emerges the previous version then newest, useful with `qa-cmp`_ + `scrub-patch`_ removes dirt from patches and may suggest improvements + `find-unresolved`_ helps find missing libraries on a stripped embedded system @@ -250,6 +251,78 @@ Results in:: Run ``repo-cd --help`` or see **repo-cd(1)** man page for details and information about known limitations. +workdir-cd +------- +Dependencies: portage (portageq), libxml2 (xmllint) + +Can be used to jump to the working directory (cd) of the specified atom. + +If ``--path=default`` workdir-cd will try to get the path from ``protageq``. +If nothing is set as PORTAGE_TMPDIR in make.conf, the usual location of +TMPDIR is ``/var/tmp/portage``. + +I unpack two packages using the ``ebuild`` command and then use the workcd +alias without an argument to navigate to the latest workdir in PORTAGE_TMPDIR:: + + ~/dev/gentoo/app-admin/entr $ eval "$(command workdir-cd --bash=workcd --path="default")" + ~/dev/gentoo/app-admin/entr $ ebuild entr-5.3-r1.ebuild unpack + Appending /home/pascal/dev/gentoo to PORTDIR_OVERLAY... + * entr-5.3.tar.gz BLAKE2B SHA512 size ;-) ... [ ok ] + >>> Unpacking source... + >>> Unpacking entr-5.3.tar.gz to /var/tmp/portage/app-admin/entr-5.3-r1/work + >>> Source unpacked in /var/tmp/portage/app-admin/entr-5.3-r1/work + ~/dev/gentoo/app-admin/entr $ ebuild entr-5.4.ebuild unpack + Appending /home/pascal/dev/gentoo to PORTDIR_OVERLAY... + * entr-5.4.tar.gz BLAKE2B SHA512 size ;-) ... [ ok ] + >>> Unpacking source... + >>> Unpacking entr-5.4.tar.gz to /var/tmp/portage/app-admin/entr-5.4/work + >>> Source unpacked in /var/tmp/portage/app-admin/entr-5.4/work + + LICENSE + + Makefile.bsd + + Makefile.linux + + Makefile.macos + + NEWS + + README.md + + configure + + data.h + + entr.c + + entr.1 + + missing + + system_test.sh + /var/tmp/portage/app-admin/entr-5.4/work/entr-5.4 $ + +If it is given a string other than '-' as the argument, workdir-cd will list +the found workdirs matching that argument:: + + / $ workcd entr + ? 1:/var/tmp/portage/app-admin/entr-5.3-r1 (default) + ? 2:/var/tmp/portage/app-admin/entr-5.4 + ? Choice? + +When the alias is ran with '-' as the argument, it will list all available workdirs +in tmpdir and ask for which one to cd into:: + + / $ workcd - + ? 1:/var/tmp/portage/app-admin/entr-5.4 (default) + ? 2:/var/tmp/portage/app-admin/entr-5.3-r1 + ? 3:/var/tmp/portage/x11-misc/xscreensaver-6.06-r2 + ? 4:/var/tmp/portage/x11-misc/xscreensaver-6.07 + ? 5:/var/tmp/portage/sys-apps/systemd-253.6 + ? Choice? + +Has some customization options, like hiding fields and running commands in +the directory (in the above case it ran ``ls`` by default), and these can +be saved in a ``workdir-cd.conf`` or like ``--path`` was above:: + + / $ workcd entr --run="echo hello world" + ? 1:/var/tmp/portage/app-admin/entr-5.3-r1 (default) + ? 2:/var/tmp/portage/app-admin/entr-5.4 + ? Choice? 1 + + hello world + +Run ``workdir-cd --help`` or see **workdir-cd(1)** man page for details and +information about known limitations. + eoldnew ------- Dependencies: portage (portageq) diff --git a/scripts/meson.build b/scripts/meson.build index 4ae4b79..65881d0 100644 --- a/scripts/meson.build +++ b/scripts/meson.build @@ -7,6 +7,7 @@ scripts = { 'qa-sed' : 'sed wrapper to detect if a call did no change', 'qa-vdb' : 'use portage VDB information to detect RDEPEND issues among others', 'repo-cd' : 'change directory based on atom then perform actions', + 'workdir-cd' : 'change directory to workdir in PORTAGE_TMPDIR then perform actions', 'scrub-patch' : 'strip patches of useless cruft and suggest improvements' } diff --git a/scripts/workdir-cd b/scripts/workdir-cd new file mode 100755 index 0000000..0c4659f --- /dev/null +++ b/scripts/workdir-cd @@ -0,0 +1,564 @@ +#!/usr/bin/env bash +. "${0%/*}"/../lib/common.bashlib || exit 1 #C# +init +depend find q sort uniq xmllint portageq +include atomf shellparse +usage <<-EOU + Usage: ${0##*/} [option]... [atom] + + Print directory location (or change directory with *Shell Integration* + below) corresponding to an atom either using portageq or a tmpdir list + given using the -P or --path option. + + atom can be partial or missing, e.g. from dev-category/example + > dev-category : print the category directory location + > example : lookup matches in all categories + > (nothing) : find the latest directory in all tmpdirs + > - : list all the workdirs in tmpdir + In all cases, will ask if multiple results unless -1 or --first is specified. + + If an atom is found, the directory is changed to the first dir of the workdir + of the package. If a category is found, the directory is changed to the category's + dir in PORTAGE_TMPDIR. + + Options: + -P, --path=PATH Colon-separated list of tmpdirs to search by priority, + special keyword "default" adds PORTAGE_TMPDIR from portageq. + (usually set in make.conf) + (e.g. --path="/var/tmp/portage", see --dumpconfig + or *Shell Integration* below to set permanently) + -D, --duplicates Allow multiple tempdirs with the same path + + -1, --first Disable interactive prompts and always pick first choice + + -F, --fuzzy Always enable fuzzy search (e.g. 'gcc' matches both 'gcc' + and 'gcc-config'), used by default if no exact name match + -f, --exact Do not return fuzzy matches even if no exact name match + (this still allows matching in multiple categories) + + -e, --exclude=LIST Comma-separated list of categories to exclude from + searches unless explicitly specified + + -q, --quiet Do not display non-error informational messages + (to be further quiet, also set --no-command) + + -R, --run=COMMAND If atom matches a package, run COMMAND then exit with + its status, see *Running Commands* for details + (default: ls -1v --color=always) + -s, --no-capture Redirect COMMAND's output to stderr rather than capture + -r, --no-command Do not run COMMAND even if defined + + --posix=ALIAS Print posix sh integration, see *Shell Integration* below + --bash=ALIAS Print bash integration with atom completion support + --fish=ALIAS Print fish integration with atom completion support + --zsh=ALIAS Print zsh integration with atom completion support + --compgen Print words for completion with a partial [atom] and exit + + -c, --no-color Disable use of colors + + --confdir=PATH Configuration dir to use instead of defaults + (@confdir@ + ${XDG_CONFIG_HOME:-~/.config}/@package@) + --dumpconfig Display config and exit (2> ${0##*/}.conf) + + -h, --help Display usage information and exit + --version Display version information and exit + + *Known Limitations* + If PORTAGE_TMPDIR is set by package.env, ${0##*/} can not find the tmpdir of + the package, since it can only get the standard PORTAGE_TMPDIR from portageq. + In this case, users can set the --path variable manually. + + *Running Commands* + If match a valid workdir, --run's COMMAND will be executed inside the directory + allowing to e.g. show directory contents (default), git status/log, + and/or display notes/reminders based on the package + name for some ideas. The variables WCD_CATEGORY, WCD_P and WCD_PACKAGE (cat/p) + are exported for use with external scripts. + + Note can set "run = eshowkw -C" in + ${XDG_CONFIG_HOME:-~/.config}/@package@/${0##*/}.conf, + or pass --run= to the eval command in *Shell Integration* below to be permanent. + + *Shell Integration* + To be able to change the directory of an interactive shell, a higher level + helper is required, i.e. if ${0##*/} prints a path, then cd to it. + + For convenience, can use one of the shell integration option, e.g. --bash=wcd + outputs code to enable \`wcd [atom]\` with atom tab completion support using + bash. Intended to be added to ~/.bashrc and/or ~/.bash_profile (can also run + in current shell to use immediately): + + eval "\$(command ${0##*/} --bash=wcd)" + + Can also pass other options like --path or --run to the above for per-aliases + effects. The above eval works the same for each supported shells, except e.g. + swap --bash for --zsh and use ~/.zshrc, or --fish and ~/.config/fish/conf.d +EOU + +setmsg 2 # stdout only used to show cd path or shell integration + +optauto args "${@}" <<-EOO + P|path=str:default: + D|duplicates=bool:false + 1|first=bool:false + F|fuzzy=bool:false + f|exact=bool:false + e|exclude=str: + q|quiet=bool:false + R|run=str:ls -1v --color=always + s|!capture=bool:true + r|!command=bool:true + posix=str: + bash=str: + fish=str: + zsh=str: + compgen=bool:false + c|!color=bool:true +EOO + +# wcd-shell_integration +# Print shell integration code using after stripping +# integration options. +wcd-shell_integration() { + local -a args=() + while (( ${#} )); do + case ${1} in + --bash|--fish|--posix|--zsh) shift 2; continue;; + --bash=*|--fish=*|--posix=*|--zsh=*) shift; continue;; + esac + args+=("${1}") + shift + done + + local pargs=( + "${WCD_SHELL}" "${0##*/}" "${args[0]+ }${args[*]@Q}" + "${WCD_SHELL}" "${0##*/}" "${args[0]+ }${args[*]@Q}" + "${WCD_SHELL}" "${WCD_SHELL}" + ) + + # pass words so that `workdir-cd --path=...` adds new paths / rules + # to compgen, may however silently fail if options are wrong + if [[ ${O[bash]} ]]; then + #!SC2016 + printf \ +'%s() { + local d + d=$(command %q%s "${@}") && [[ -n ${d} ]] && cd "${d}" +} +_%s() { + mapfile -t COMPREPLY < <(command %q%s "${COMP_WORDS[@]:1:COMP_CWORD-1}" --compgen -- "${2}" 2>/dev/null) +} +complete -F _%s %s\n' "${pargs[@]}" + elif [[ ${O[fish]} ]]; then + #!SC2016 + printf \ +'function %s + set --local d (command %q%s $argv) && [ -n "$d" ] && cd $d +end +function _%s + set --local w (commandline -op) + command %q%s $w[2..-2] --compgen -- $w[-1] 2>/dev/null +end +complete -e %s +complete -fa "(_%s)" %s\n' "${pargs[@]}" "${WCD_SHELL}" + elif [[ ${O[posix]} ]]; then + #!SC2016 + printf \ +'%s() { + _workdir_cd="$(command %q%s "${@}")" && [ -n "${_workdir_cd}" ] && cd "${_workdir_cd}" + unset _workdir_cd +}\n' "${pargs[@]:0:3}" + elif [[ ${O[zsh]} ]]; then + #!SC2016 + printf \ +'%s() { + local d + d=$(command %q%s "${@}") && [[ -n ${d} ]] && cd "${d}" +} +_%s() { + local w + read -cA w + reply=(${(f)"$(command %q%s "${w[@]:1:-1}" --compgen -- "${w[-1]}" 2>/dev/null)"}) +} +compctl -K _%s %s\n' "${pargs[@]}" + fi +} + +WCD_SHELL=${O[bash]:-${O[fish]:-${O[posix]:-${O[zsh]}}}} +if [[ ${WCD_SHELL} ]]; then + [[ ${O[bash]:+x}${O[fish]:+x}${O[posix]:+x}${O[zsh]:+x} > x ]] && + die "cannot specify more than one shell integration option" + wcd-shell_integration "${@}" + exit +fi + +set -- "${args[@]}"; unset args +(( ${#} <= 1 )) || die "too many atoms given, see \`${0##*/} --help\`" + +[[ ${O[exclude]} =~ ^[,A-Za-z0-9+_.-]*$ ]] \ + || die "--exclude list has invalid characters for categories" + +${O[fuzzy]} && ${O[exact]} \ + && die "-F/--fuzzy and -f/--exact cannot be specified together" + +[[ ${O[run]} ]] || O[command]=false + +# wcd-ask ... +# Ask to pick from and store result in . +# If only one choice, do nothing beside set to it. +# If choice starts with ${HOME}, replaces with ~ only when displaying. +wcd-ask() { + local -n outref=${1} + shift + if ${O[first]} || (( ${#} == 1 )); then + outref=${1} + return 0 + fi + local default=" ${C[a]}(default)${C[n]}" + local display + local -i i + for ((i=1; i<=${#}; i++)); do + wcd-get_display_path display c lr "${!i}" + wcd-msg '?:m' "${C[y]}${i}${C[a]}:${display}${default}" + default= + done + + wcd-msg -n '?:y' "Choice? " + + local REPLY + read -ren ${##} + printf -v i %u "${REPLY}" 2>/dev/null + (( i <= 1 || i > ${#} )) && i=1 # default if non-integer / invalid reply + + outref=${!i} #!SC2034 +} + +# wcd-cd [atom] +# Print directory for atom and change directory. +# If no atom, search for latest workdir in all tmpdirs. +# If atom is ., list all workdirs in tmpdir and let user choose. +# Exit with value 104 if failed to find a directory to use. +# Return 1 if resulting directory is not a package directory. +wcd-cd() { + local cd + if (( ! ${#} )); then + # if no atom, search for latest workdir in all tmpdirs + set +f # enable globbing + #!SC2068 + #!SC2012 + wcd-ask cd "$(ls -td ${TEMPDIRPATH[@]/%/"/*/*"} 2>/dev/null | head -n1)" + set -f # disable globbing + elif [[ ${1} == - ]]; then + # list all workdirs in tmpdir and let user choose + cd= + set +f # enable globbing + #!SC2068 + #!SC2046 + #!SC2012 + wcd-ask cd $(ls -td ${TEMPDIRPATH[@]/%/"/*/*"} 2>/dev/null) + set -f # disable globbing + else + local search=${1%/} # trim / in case did tab-complete on a directory + + # most atom elements are unused, but atomsp can sanitize input + local atom + atomsp atom "${search}" && [[ ${atom[3]} ]] \ + || die "invalid search '${search}'" + + local cat=${atom[2]:-} + local name=${atom[3]} # can be a category-only search + + # search paths separately to sort each individually in TEMPDIRPATH order + local path + local -a choice=() + ${O[exact]} && search=${name} || search="*${name}*" + search=${search//[_-]/[_-]} # "case" insensitive _ and - + for path in "${TEMPDIRPATH[@]}"; do + if [[ ${cat} && -d ${path}/${cat}/${name} ]] && ! ${O[fuzzy]}; then + # exact match and no fuzzy, print as-is ignoring exceptions + printf '%s\0' "${path}/${cat}/${name}" + else + wcd-find 1 2 "${path}" -iname "${search}" -print0 | sort -z || die + fi + done | mapfile -td '' choice + + if (( ! ${#choice[@]} )); then + wcd-msg '!:r' "no non-excluded match found for '${C[m]}${name}${C[n]}'" + exit 104 + fi + + if ! ${O[fuzzy]} && ! ${O[exact]}; then + # only do fuzzy if no exact name matches when fuzziness is + # undefined, and post-process to avoid running find(1) twice + local base + local -a exact=() + for path in "${choice[@]}"; do + base=${path##*/} + base=${base//[_-]/[_-]} + [[ ${name,,} == ${base,,} ]] && exact+=("${path}") #!SC2053 + done + (( ${#exact[@]} )) && choice=("${exact[@]}") + fi + + wcd-ask cd "${choice[@]}" + fi + + cd "${cd}" || die "failed to cd '${cd}'" + if ${O[command]} || ! ${O[quiet]}; then + wcd-set_atom + fi + # if we ended up in a tempdir, cd into work and the first thing ls returns + if [[ -e work ]]; then + cd="${cd}"/work + cd "${cd}" || die "failed to cd '${cd}'" + local firstdir + #!SC2012 + firstdir="$(ls -1 | head -n1)" + if [[ -e "${firstdir}" ]]; then # normally work should not be empty, but just in case + cd="${cd}/${firstdir}" + cd "${cd}" || die "failed to cd '${cd}'" + fi + fi + echo "${cd}" # for the interactive shell to use + + # return true if we successfully changed dir + [[ "${cd}" == "${PWD}" ]]; +} + +# wcd-find ... +# Run find(1) with preset exclusion/depth/directory rules on paths. +# should contain the action and optionally extra rules, +# e.g. -ipath '*/category/package' -print0 +wcd-find() { + local depth=(-mindepth "${1}" -maxdepth "${2}") + shift 2 + + local -a paths=() + while (( ${#} )); do + [[ ${1::1} == - ]] && break + paths+=("${1}") + shift + done + + local omitdirs + split omitdirs "${O[exclude]}" ',' + + local omit=( -name '.*' ) # .git and other hidden files + + local dir path + for path in "${paths[@]}"; do + # add path to ensure doesn't match on packages themselves + # (loop over given can't safely rely on word splitting to /#/-o -path}) + for dir in "${omitdirs[@]}"; do + omit+=(-o -path "${path}/${dir}") + done + done + + # not trying to use pquery/portage/bashglobs given find(1) is faster + # and makes this simpler for ordering and custom --path tmpdirs + find -H "${paths[@]}" "${depth[@]}" \ + -type d \( "${omit[@]}" \) -prune -o -type d "${@}" || die +} + +# wcd-get_display_path +# Set with adjusted for display with mixed colors +# for cat/pkg to stand out and ${HOME} be replaced by ~. +wcd-get_display_path() { + local -n outref=${1} + + local path_cat path_pn path_base + path_cat=${4%/*} + path_cat=${path_cat##*/} + path_pn=${4##*/} + path_base=${4%"${path_cat}/${path_pn}"} + + if [[ ! ${TEMPDIRNAME[${path_base%/}/]+x} ]]; then + path_cat=${path_pn} + path_pn= + path_base=${4%"${path_cat}"} + if [[ ! ${TEMPDIRNAME[${path_base%/}/]+x} ]]; then + path_cat= + path_base=${4} + fi + fi + + outref=${C[${2}]}${path_base/#"${HOME}"/\~}${path_cat:+${C[${3}]}${path_cat}${path_pn:+/${path_pn}}}${C[n]} +} + +# wcd-get_expand_tilde +# Expand ~ in then store back in +wcd-get_expand_tilde() { + local -n outref=${1} + if [[ ${2::1} == '~' ]]; then + # use printf %q to escape anything potentially harmful then unescape ~ + # and let the shell handle all use-cases, e.g. ~user/ ~+/ etc... + # Not that there's a real need for safety here, but it doesn't hurt. + printf -v outref %q "${2}" + eval "outref=${outref/#\\\~/\~}" + else + outref=${2} + fi +} + +# wcd-msg [-n] [::[message-color-id]] ... +# Print message with prefix, e.g. msg '!:r' "prefixed by red !". +# Multiple arguments will result in multiple lines with same prefix. +# Skip trailing newline if -n. +# More complex coloring can be used in the message itself. +wcd-msg() { + local trail=1 + if [[ ${1} == -n ]]; then + trail= + shift + fi + + local -a m + split m "${1}" ':' + shift + + printf " ${m[1]+${C[${m[1]}]}}${m[0]}${m[1]+${C[n]}} ${m[2]+${C[${m[2]}]}}%s${m[2]+${C[n]}}${trail:+\n}" \ + "${@}" >&2 +} + +# wcd-print_compgen [current] +# Print possible words for tab completion based on TEMPDIRPATH. +wcd-print_compgen() { + # do nothing if no arguments rather print the rather slow full list + (( ${#} )) || return 0 + + local -a gen + local search=${1//[_-]/[_-]} # "case" insensitive _ and - + { + if [[ ${1} == */* ]]; then + wcd-find 2 2 "${TEMPDIRPATH[@]}" -ipath "*/${search}*" -printf '%P\n' + else + # process categories separately to add / + wcd-find 1 1 "${TEMPDIRPATH[@]}" -iname "${search}*" -printf '%f/\n' + wcd-find 2 2 "${TEMPDIRPATH[@]}" -iname "${search}*" -printf '%f\n' + fi + } | sort | uniq | map gen + + # if _ or - had an exact match, discard the mismatching ones as the shell + # will not know how to handle this + local exactmatch=false + if [[ ${1} == *[_-]* ]]; then + local match + for match in "${gen[@]}"; do + if [[ ${match} == "${1}"* ]]; then + exactmatch=true + echo "${match}" + fi + done + fi + ${exactmatch} || printarray gen +} + +# wcd-print_command +# Run ${O[run]}, output through wcd-msg, and return its exit status +wcd-print_command() { + local -x WCD_CATEGORY=${PWD%/*/*} + WCD_CATEGORY=${WCD_CATEGORY##*/} + local -x WCD_P=${PWD%/*} + WCD_P=${WCD_P##*/} + local -x WCD_PACKAGE=${WCD_CATEGORY}/${WCD_P} + + # roughly allow tilde expansion for any arguments + local arg + local -a cmd=() + for arg in ${O[run]}; do + wcd-get_expand_tilde arg "${arg}" + cmd+=("${arg}") + done + + if ${O[capture]}; then + command -- "${cmd[@]}" |& map output + (( ret=PIPESTATUS[0] )) && mod='-:r' || mod='+:g' + (( ${#output[@]} )) && wcd-msg "${mod}" "${output[@]}" + return ${ret} + fi + + "${cmd[@]}" >&2 +} + +# wcd-set_atom +# Fill ATOM array for all valid packages in the current directory. +# (if any). Each element has a atomsp() +declare -a ATOM=() +wcd-set_atom() { + local category + local atom + atom=$(basename "${PWD}") + category=$(basename "${PWD%/*}") + + ATOM+=( "${category}/${atom/%-*/}" ) +} + +# wcd-set_tempdirpath +# Set TEMPDIRPATH array based on O[path] string. Also sets TEMPDIRNAME[${path%/}/] +# for name lookup +declare -A TEMPDIRNAME +declare -a TEMPDIRPATH=() +wcd-set_tempdirpath() { + local -A nameindex + local -a paths + split paths "${O[path]}" ':' + + local path + local found_default + found_default="false" + for path in "${paths[@]}"; do + [[ ${path} ]] || continue + + if [[ ${path} == default ]]; then + path="$(portageq envvar PORTAGE_TMPDIR)/portage" + if [[ ${path} ]]; then + _wcd-set_tempdirpath_add_if_unique + found_default="true" + fi + if ! "${found_default}"; then + path="/var/tmp/portage" # if nothing was found using portageq, use the default + _wcd-set_tempdirpath_add_if_unique + fi + else + # allow tilde expansion for convenience + wcd-get_expand_tilde path "${path}" + _wcd-set_tempdirpath_add_if_unique + fi + done + + + (( ${#TEMPDIRPATH[@]} )) || die "no valid tmpdir paths found, see \`${0##*/} --help\`" +} + +_wcd-set_tempdirpath_add_if_unique() { + if absdir path; then + name=${path} + if [[ ! ${nameindex[${name}]+x} ]] || ${O[duplicates]}; then + TEMPDIRNAME[${path%/}/]=${name} + TEMPDIRPATH+=("${path}") + nameindex[${name}]= + fi + else + wcd-msg '!:b:a' "${path}: ignored path (not a directory)" + fi +} + +cd / # for running in a removed directory +wcd-set_tempdirpath +if ${O[compgen]}; then + wcd-print_compgen "${@}" +elif wcd-cd "${@}"; then + # avoid doing some slower operations if not needed + if ${O[command]} || ! ${O[quiet]}; then + if (( ${#ATOM[@]} )); then + if ${O[command]}; then + wcd-print_command + exit ${?} + fi + fi + fi +fi + +: + +# vim: ts=4 diff --git a/tests/workdir-cd/bash b/tests/workdir-cd/bash new file mode 100644 index 0000000..a514d72 --- /dev/null +++ b/tests/workdir-cd/bash @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +PATH=${SCRIPT%/*}:${PATH} +func=${SCRIPT##*/} + +eval "$(command "${func}" -cr1 --bash="${func}" --path="${DATADIR}/tempdir/portage")" +eval "$(command "${func}" -cr1 --posix "${func}-posix" --path="${DATADIR}/tempdir/portage")" + +declare -f "${func}" "_${func}" "${func}-posix" + +rm -rf ${DATADIR}/tempdir/portage/dev-test/newest-1.0 +mkdir -p ${DATADIR}/tempdir/portage/dev-test/newest-1.0/work + +"${func}" +[[ ${PWD} == */newest-1.0/work ]] || fail "${PWD} is not in */tempdir/portage/dev-test/newest-1.0/work" +rm -rf ${DATADIR}/tempdir/portage/dev-test/newest + +"${func}-posix" test +[[ ${PWD} == */portage/dev-test/test/work ]] || fail "${PWD} is not in */portage/dev-test/test" + +COMP_WORDS=("${func}" -c -1 te) +COMP_CWORD=3 +COMPREPLY=() +_"${func}" '' te +[[ ${COMPREPLY[0]:-} == test ]] || fail "COMPREPLY is not 'test'" diff --git a/tests/workdir-cd/compgen b/tests/workdir-cd/compgen new file mode 100644 index 0000000..d9e2f92 --- /dev/null +++ b/tests/workdir-cd/compgen @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +TEMPDIRPATH=${PWD}/tempdir +mkdir -p tempdir/profiles + +mkdir -p tempdir/dev-{test,alt}/{a,b}{a,b}test +mkdir -p tempdir/dev-test/extra + +testcomp() { + local word=${1} + shift + expect -s "'$(printf %s "${@/%/$'\n'}")'" -cr1P "${TEMPDIRPATH}" --compgen ${word} +} + +testcomp '' '' +testcomp a aatest abtest +testcomp aa aatest +testcomp dev dev-alt/ dev-test/ +testcomp dev-t dev-test/ +testcomp dev-test/ex dev-test/extra diff --git a/tests/workdir-cd/help b/tests/workdir-cd/help new file mode 100644 index 0000000..c7fe1a6 --- /dev/null +++ b/tests/workdir-cd/help @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +expect -2 "Usage: ${SCRIPT##*/} *" --help diff --git a/tests/workdir-cd/meson.build b/tests/workdir-cd/meson.build new file mode 100644 index 0000000..670d2fa --- /dev/null +++ b/tests/workdir-cd/meson.build @@ -0,0 +1,9 @@ +tests = [ + 'bash', + 'compgen', + 'help', + 'run', + 'search', + 'shellcheck', + 'tmpdirpath' +] diff --git a/tests/workdir-cd/run b/tests/workdir-cd/run new file mode 100644 index 0000000..b3546db --- /dev/null +++ b/tests/workdir-cd/run @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +TEMPDIRPATH=${DATADIR}/tempdir/portage + +expect -2 -s "*' +work +extra line'" -c1P "${TEMPDIRPATH}" --no-capture --run="${BASH}" test <<<' + echo something + echo ${PWD##*/} + echo extra line' + +xfail 100 expect -2 -s "*' - dev-test test dev-test/test'" \ + -c1P "${TEMPDIRPATH}" -R "${BASH}" test <<<' + echo "${WCD_CATEGORY} ${WCD_P} ${WCD_PACKAGE}" + exit 100' + +expect -2 -! "*badoutput*" -c1P "${TEMPDIRPATH}" --no-command -sR "${BASH}" test <<<' + echo badoutput' + +HOME=${PWD}/home +mkdir home +echo -n 'hello' > home/file1 +echo -n ' world' > home/file2 +echo 'cat "${1}" "${3}"; echo "${2}"' > home/testcmd +chmod +x home/testcmd +expect -2 "*hello world!*" -c1P "${TEMPDIRPATH}" --run="~/testcmd ~/file1 ! ~/file2" test diff --git a/tests/workdir-cd/search b/tests/workdir-cd/search new file mode 100644 index 0000000..a563c77 --- /dev/null +++ b/tests/workdir-cd/search @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +func=${SCRIPT##*/} +TEMPDIRPATH=${PWD}/high:${PWD}/low +mkdir -p {high,low,dup}/profiles + +mkdir -p high/dev-alt/alt-1.0/work +mkdir -p high/virtual/test-1.0/work/upstream-dir-1.1 +mkdir -p low/{dev-test/test{-1.0,-fuzzy-1.0}/work,dev-alt/alt-1.0/work} + +expect '*/high/virtual/test-1.0/work/upstream-dir-1.1' -cr1P "${TEMPDIRPATH}" dev-test/test +expect '*/low/dev-test' -cr1P "${TEMPDIRPATH}" dev-test +expect '*/high/virtual/test-1.0/work/upstream-dir-1.1' -cr1P "${TEMPDIRPATH}" test + +expect '*/low/dev-test/test-fuzzy-1.0/work' -cr1P "${TEMPDIRPATH}" test_fuzzy +expect '*/low/dev-test/test-fuzzy-1.0/work' -cr1P "${TEMPDIRPATH}" fuzzy +expect '*/high/virtual/test-1.0/work/upstream-dir-1.1' -cr1P "${TEMPDIRPATH}" --fuzzy test +xfail 104 expect -2 "*no non-excluded match found*" -cr1P "${TEMPDIRPATH}" --exact fuzzy + +expect '*/high/virtual/test-1.0/work/upstream-dir-1.1' -cr1P "${TEMPDIRPATH}" virtual/test +expect '*/high/virtual/test-1.0/work/upstream-dir-1.1' -cr1P "${TEMPDIRPATH}" --exclude='' test + +expect '*/high/dev-alt' -cr1P "${TEMPDIRPATH}" alt + +xfail 104 expect "''" -cr1P "${TEMPDIRPATH}" --exclude='metadata' metadata +xfail 1 expect -2 "*Error: invalid search*" -cr1P "${TEMPDIRPATH}" .git + +mkdir -p low/dev-alt/alt-2.0/work +expect '*dev-alt/alt-2.0*' -cr1P "${TEMPDIRPATH}" - +mkdir -p high/dev-alt/alt-3.0/work +expect '*dev-alt/alt-3.0*' -cr1P "${TEMPDIRPATH}" - diff --git a/tests/workdir-cd/shellcheck b/tests/workdir-cd/shellcheck new file mode 100644 index 0000000..50cfea4 --- /dev/null +++ b/tests/workdir-cd/shellcheck @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +shellcheck diff --git a/tests/workdir-cd/tmpdirpath b/tests/workdir-cd/tmpdirpath new file mode 100644 index 0000000..b608bc7 --- /dev/null +++ b/tests/workdir-cd/tmpdirpath @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set_portroot + + +mkdir -p root/tmp/portage/dev-default/test/work/test-1.0 + +expect '*/tmp/portage/dev-default/test/work/test-1.0' -cr1P "default" dev-test/test +expect '*/tempdir/portage/dev-test/test/work' -cr1P "${DATADIR}/tempdir/portage" dev-test/test