Use dynamic dispatch for scripting libs

main
Buddy Sandidge 1 month ago
parent bbe64105e4
commit d1b095f98c

@ -54,6 +54,11 @@ source <(trubka --completion-script-bash)
source <(rbenv init -)
#{{ end -}}
#{{ if stat (joinPath .chezmoi.homeDir ".sdkman/bin/sdkman-init.sh") -}}
export SDKMAN_DIR="$HOME/.sdkman"
source "#{{ .chezmoi.homeDir }}/.sdkman/bin/sdkman-init.sh"
#{{ end -}}
#{{ if and (lookPath "rvm") (stat (joinPath .chezmoi.homeDir ".rvm" "bin") ) -}}
if [[ ! "${PATH}" =~ ${HOME}/.rvm/bin ]]; then
export PATH="$PATH:$HOME/.rvm/bin"

@ -1,3 +1,92 @@
source_env_file() {
if [ -f "${1}" ]; then
return
fi
eval "$(
grep -v '^\s*\#' "${1}" |
grep -v '^\s*$' |
sed 's/^\s*export//g' |
sed 's/^/export /g'
)"
}
_run_require() (
import=${1}
shift
set -euo pipefail
source "${XDG_DATA_HOME}/buddy/include.bash"
require "${import}"
"${import//\//_}" "$@"
)
add_date_prefix() (
_run_require add_date_prefix "$@"
)
command_installed() (
_run_require assert/command "$@"
)
gem_env () (
_run_require gem_env "$@"
)
get_bitrate() (
_run_require get_bitrate "$@"
)
get_create_date() (
date -r "$1" +"%F"
)
github_install_scripts () (
_run_require github/install_scripts "$@"
)
github_install_scripts_latest_releases() (
_run_require github/install_scripts_latest_releases "$@"
)
github_latest_release() (
_run_require github/latest_release "$@"
)
github_releases() (
_run_require github/releases "$@"
)
github_tags() (
_run_require github/tags "$@"
)
go_deps() (
_run_require go_deps "$@"
)
json_to_yaml() (
_run_require json_to_yaml "$@"
)
yaml_to_json() (
_run_require yaml_to_json "$@"
)
make_script() (
_run_require make_script "$@"
)
min_jpg() (
_run_require min_jpg "$@"
)
min_png() (
_run_require min_png "$@"
)
slugify() (
_run_require slugify "$@"
)
function min-jpg {
min_jpg "${@}"
}

@ -14,196 +14,80 @@ source_env_file() {
)"
}
make_scripts() (
for FILE in "$@"; do
make_script "${FILE}"
done
_run_require() (
import=${1}
shift
fn=$(echo "${import}" | sed 's|/|_|g')
set -eu
. "${XDG_DATA_HOME}/buddy/include.sh"
require "${import}"
${fn} "$@"
)
go_deps() (
GO_MOD=${1:-go.mod}
START_AT=$(grep -n '^require ($' "${GO_MOD}" | awk -F : '{print $1}' | head -n 1)
END_AT=$(grep -n '^)$' "${GO_MOD}" | awk -F : '{print $1}' | head -n 1)
awk 'NR>'"${START_AT}"' && NR<'"${END_AT}"' {print $1}' "${GO_MOD}"
add_date_prefix () (
_run_require add_date_prefix "$@"
)
command_installed () (
_run_require assert/command "$@"
)
#{{ if lookPath "gem" -}}
#{{ if lookPath "dasel" -}}
#{{ if lookPath "jq" -}}
gem_env () (
NAME=${1:-}
if [ "$NAME" != '' ]; then
gem_env_json | jq --raw-output '.["'"${NAME}"'"]'
else
gem_env_json
fi
_run_require gem_env "$@"
)
gem_env_json () (
gem environment \
| dasel --read yaml --write json \
| jq '.["RubyGems Environment"] | to_entries | map((.value | keys)[0] as $key | {key: $key, value: .value[$key] }) | from_entries'
get_bitrate () (
_run_require get_bitrate "$@"
)
#{{- end }}
#{{- end }}
#{{- end }}
make_script() (
cat <<-EOF > "${1}"
#!/usr/bin/env bash
get_create_date () (
date -r "$1" +"%F"
)
set -euo pipefail
github_install_scripts () (
_run_require github/install_scripts "$@"
)
main() (
echo "todo"
)
github_install_scripts_latest_releases () (
_run_require github/install_scripts_latest_releases "$@"
)
main "$@"
EOF
chmod +x "${1}"
github_latest_release () (
_run_require github/latest_release "$@"
)
add_date_prefix() (
DIR=$(dirname "$1")
FILE=$(basename "$1")
DATE=$(date -r "$1" +"%F")
if [ ! -f "$1" ]; then
echo "unknown file: $1"
exit 1
fi
mv "$1" "$DIR/${DATE}_${FILE}"
github_releases () (
_run_require github/releases "$@"
)
get_bitrate() (
if [ ! -f "$1" ]; then
echo "[ERROR] unknown file: $1"
exit 1
fi
command_installed exiftool
exiftool -AudioBitrate "$1" | awk '{print $4}'
)
json_to_yaml() (
ARG=${1-}
command_installed bat dasel
if [ -z "${ARG}" ]; then
dasel --read json --write yaml | bat --language yaml
elif [ -f "${ARG}" ]; then
dasel --read json --write yaml --file "${ARG}" | bat --language yaml
else
echo "${ARG}" | dasel --read json --write yaml | bat --language yaml
fi
github_tags () (
_run_require github/tags "$@"
)
yaml_to_json() (
ARG=${1-}
command_installed bat dasel
if [ -z "${ARG}" ]; then
dasel --read yaml --write json | bat --language json
elif [ -f "${ARG}" ]; then
dasel --read yaml --write json --file "${ARG}" | bat --language json
else
echo "${ARG}" | dasel --read yaml --write json | bat --language json
fi
go_deps () (
_run_require go_deps "$@"
)
#{{ if lookPath "rg" -}}
github_install_scripts () (
rg github.com "${HOME}/.local/bin" \
| sed 's/:/ /' \
| sed 's/=/ /g' \
| awk '{print $3}' \
| sed 's|https://github.com/||g' \
| sed 's|/| |g' \
| awk '{print $1 " " $2}' \
| sed 's| |/|g' \
| sort \
| uniq
)
github_install_scripts_latest_releases() (
for REPO in $(github_install_scripts); do
echo "${REPO} $(github_latest_release "${REPO}")"
done
)
#{{- end }}
github_latest_release() (
github_tags "$@" | head -n 1
)
github_releases() (
USER=${1}
REPO=${2-}
command_installed curl
if [ -z "${REPO}" ]; then
USER=$(echo "${1}" | sed 's|/| |g' | awk '{print $1}')
REPO=$(echo "${1}" | sed 's|/| |g' | awk '{print $2}')
fi
curl --silent --header "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/${USER}/${REPO}/releases"
json_to_yaml () (
_run_require json_to_yaml "$@"
)
github_tags() (
USER=${1}
REPO=${2-}
command_installed curl jq
if [ -z "${REPO}" ]; then
USER=$(echo "${1}" | sed 's|/| |g' | awk '{print $1}')
REPO=$(echo "${1}" | sed 's|/| |g' | awk '{print $2}')
fi
curl --silent --header "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/${USER}/${REPO}/tags" |
jq -r '.[].name'
yaml_to_json () (
_run_require yaml_to_json "$@"
)
command_installed() (
set -ue
for cmd in "${@}"; do
if ! command -v "${cmd}" 1>&2 >/dev/null; then
echo "${cmd} not installed" 1>&2
exit 1
fi
done
)
min_jpg() (
set -ue
command_installed jpegtran
for FILE in "$@" ; do
PERMISSIONS=$(stat --format '%a' "${FILE}")
OUT_FILE=$(mktemp)
jpegtran -optimize -perfect -outfile "${OUT_FILE}" "${FILE}"
mv "${OUT_FILE}" "${FILE}"
chmod "${PERMISSIONS}" "${FILE}"
done
)
min_png() (
set -ue
command_installed pngcrush
for FILE in "$@" ; do
PERMISSIONS=$(stat --format '%a' "${FILE}")
OUT_FILE=$(mktemp)
pngcrush -rem alla -reduce -brute "${FILE}" "${OUT_FILE}"
mv "${OUT_FILE}" "${FILE}"
chmod "${PERMISSIONS}" "${FILE}"
done
)
get_create_date() (
date -r "$1" +"%F"
make_script () (
_run_require make_script "$@"
)
#shellcheck disable=SC2120
slugify() (
if [ "$#" -ne 0 ]; then
#shellcheck disable=SC2119
echo "$@" | slugify
else
iconv --to-code ascii//TRANSLIT |
sed -E 's/[^a-zA-Z0-9]+/-/g' |
sed -E 's/^-+\|-+$//g' |
tr '[:upper:]' '[:lower:]'
fi
min_jpg () (
_run_require min_jpg "$@"
)
min_png () (
_run_require min_png "$@"
)
slugify () (
_run_require slugify "$@"
)

@ -20,7 +20,7 @@ _append_path () {
PATH="${PATH}:${1}"
}
_append_path "/usr/local/opt/mysql-client/bin"
_append_path /usr/local/opt/mysql-client/bin
_append_path "${HOME}/google-cloud-sdk/bin"
unset _append_path

@ -68,6 +68,11 @@ if [ "${RBENV_SHELL}" = "" ]; then
fi
#{{ end -}}
#{{ if stat (joinPath .chezmoi.homeDir ".sdkman/bin/sdkman-init.sh") -}}
export SDKMAN_DIR="$HOME/.sdkman"
source "#{{ .chezmoi.homeDir }}/.sdkman/bin/sdkman-init.sh"
#{{ end -}}
#{{- if lookPath "rvm" }}
if [[ -d "${HOME}/.rvm/bin" ]]; then
if [[ ! "${PATH}" =~ "${HOME}/.rvm/bin" ]]; then

@ -16,7 +16,7 @@ function get_os () (
APP=deno
VERSION=${VERSION:-1.45.5}
VERSION=${VERSION:-2.0.6}
DEST=${XDG_DATA_HOME}/apps/releases/${APP}
URL=https://github.com/denoland/deno/releases/download/v${VERSION}/deno-$(uname -m)-$(get_os).zip

@ -6,7 +6,7 @@ set -euo pipefail
source "${XDG_DATA_HOME}/buddy-up/includes/utils.sh"
APP=mockery
VERSION=${VERSION:-2.43.2}
VERSION=${VERSION:-2.46.3}
DEST="${XDG_DATA_HOME}/apps/releases/${APP}"
URL=https://github.com/vektra/mockery/releases/download/v${VERSION}/mockery_${VERSION}_$(uname)_$(uname -m).tar.gz

@ -1,10 +1,22 @@
if [ -v _LOADED_LIBS ]; then
return
fi
_LOADED_LIBS=()
include() {
local file
for file in "${@}"; do
if [[ -f "${XDG_DATA_HOME}/buddy/lib/${file}.bash" ]]; then
# shellcheck disable=SC1090
source "${XDG_DATA_HOME}/buddy/lib/${file}.bash"
elif [[ -f "${XDG_DATA_HOME}/buddy/lib/${file}.sh" ]]; then
# shellcheck disable=SC1090
source "${XDG_DATA_HOME}/buddy/lib/${file}.sh"
else
>&2 echo "include unknown: ${file}"
exit 1
fi
done
}
require() {
local file
local previous
for file in "${@}"; do
@ -13,14 +25,7 @@ include() {
continue 2
fi
done
_LOADED_LIBS+=("$file")
if [[ -f "${XDG_DATA_HOME}/buddy/lib/$file.bash" ]]; then
# shellcheck disable=SC1090
source "${XDG_DATA_HOME}/buddy/lib/${file}.bash"
else
>&2 echo "unknown: ${file}"
exit 1
fi
_LOADED_LIBS+=("${file}")
include "${file}"
done
}

@ -0,0 +1,36 @@
LOADED_LIBS=
include() {
local file
for file in "${@}"; do
if [ -f "${XDG_DATA_HOME}/buddy/lib/${file}.sh" ]; then
# shellcheck disable=SC1090
. "${XDG_DATA_HOME}/buddy/lib/${file}.sh"
else
>&2 echo "include unknown: ${file}"
exit 1
fi
done
}
require() {
local file
local previous
for file in "${@}"; do
echo "$LOADED_LIBS" | while read -r previous; do
if [ -z "${previous}" ]; then
continue
elif [ "${previous}" = "${file}" ]; then
continue 2
fi
done
if [ -z "${LOADED_LIBS}" ]; then
LOADED_LIBS=${file}
else
LOADED_LIBS="${LOADED_LIBS}\n${file}"
fi
include "${file}"
done
}

@ -0,0 +1,8 @@
require bail
add_date_prefix () (
if [ ! -f "$1" ]; then
bail "unknown file: $1"
fi
mv "$1" "$(dirname "$1")/$(date -r "$1" +"%F")_$(basename "$1")"
)

@ -0,0 +1,9 @@
require bail
assert_command() (
for cmd in "${@}"; do
if ! command -v "${cmd}" 1>&2 >/dev/null; then
bail "command not installed: ${cmd}"
fi
done
)

@ -0,0 +1,7 @@
require bail
must_be_root() (
if [ "$(id --user)" != 0 ]; then
bail "must run as root"
fi
)

@ -0,0 +1,4 @@
bail() (
echo >&2 "$@"
exit 1
)

@ -0,0 +1,3 @@
file_age_in_seconds() (
echo $(($(date +"%s") - $(stat -c "%Y" "${1}")))
)

@ -0,0 +1,17 @@
require assert/command
assert_command dasel gem jq
gem_env() (
NAME=${1:-}
if [ "${NAME}" ]; then
gem environment |
dasel --read yaml --write json |
jq '.["RubyGems Environment"] | to_entries | map((.value | keys)[0] as $key | {key: $key, value: .value[$key] }) | from_entries' |
jq --raw-output '.["'"${NAME}"'"]'
else
gem environment |
dasel --read yaml --write json |
jq '.["RubyGems Environment"] | to_entries | map((.value | keys)[0] as $key | {key: $key, value: .value[$key] }) | from_entries'
fi
)

@ -0,0 +1,11 @@
require assert/command
require bail
assert_command exiftool
get_bitrate() (
if [ ! -f "$1" ]; then
bail "unknown file: $1"
fi
exiftool -AudioBitrate "$1" | awk '{print $4}'
)

@ -0,0 +1,7 @@
github_download_url() (
USER=$1
REPO=$2
TAG=$3
FILE=$4
echo "https://github.com/$USER/$REPO/releases/download/v${TAG}/${FILE}"
)

@ -0,0 +1,16 @@
require assert/command
assert_command rg
github_install_scripts() (
rg github.com "${HOME}/.local/bin" |
sed 's/:/ /' |
sed 's/=/ /g' |
awk '{print $3}' |
sed 's|https://github.com/||g' |
sed 's|/| |g' |
awk '{print $1 " " $2}' |
sed 's| |/|g' |
sort |
uniq
)

@ -0,0 +1,8 @@
require github/install_scripts
require github/latest_release
github_install_scripts_latest_releases() (
for REPO in $(github_install_scripts); do
echo "${REPO} $(github_latest_release "${REPO}")"
done
)

@ -0,0 +1,5 @@
require github/tags
github_latest_release() (
github_tags "$@" | head -n 1
)

@ -0,0 +1,14 @@
require file_age_in_seconds
require mkdir_if_missing
github_release_json() (
PROJECT=$1
DAY_IN_SECONDS=$((60 * 60 * 24))
JSON=${XDG_CACHE_HOME}/apps/meta/${PROJECT}/releases.json
mkdir_if_missing "$(dirname "${JSON}")"
FILE_AGE=$(file_age_in_seconds "${JSON}")
if [[ ! -f ${JSON} || ${FILE_AGE} -gt ${DAY_IN_SECONDS} ]]; then
curl --silent --output "${JSON}" "https://api.github.com/repos/${PROJECT}/releases"
fi
echo "${JSON}"
)

@ -0,0 +1,14 @@
require assert/command
assert_command curl
github_releases() (
USER=${1}
REPO=${2-}
if [ -z "${REPO}" ]; then
USER=$(echo "${1}" | sed 's|/| |g' | awk '{print $1}')
REPO=$(echo "${1}" | sed 's|/| |g' | awk '{print $2}')
fi
curl --silent --header "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/${USER}/${REPO}/releases"
)

@ -0,0 +1,15 @@
require assert/command
assert_command curl jq
github_tags() (
USER=${1}
REPO=${2-}
if [ -z "${REPO}" ]; then
USER=$(echo "${1}" | sed 's|/| |g' | awk '{print $1}')
REPO=$(echo "${1}" | sed 's|/| |g' | awk '{print $2}')
fi
curl --silent --header "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/${USER}/${REPO}/tags" |
jq -r '.[].name'
)

@ -0,0 +1,6 @@
go_deps() (
GO_MOD=${1:-go.mod}
START_AT=$(grep -n '^require ($' "${GO_MOD}" | awk -F : '{print $1}' | head -n 1)
END_AT=$(grep -n '^)$' "${GO_MOD}" | awk -F : '{print $1}' | head -n 1)
awk 'NR>'"${START_AT}"' && NR<'"${END_AT}"' {print $1}' "${GO_MOD}"
)

@ -0,0 +1,14 @@
require assert/command
assert_command bat dasel
json_to_yaml() (
ARG=${1-}
if [ -z "${ARG}" ]; then
dasel --read json --write yaml | bat --language yaml
elif [ -f "${ARG}" ]; then
dasel --read json --write yaml --file "${ARG}" | bat --language yaml
else
echo "${ARG}" | dasel --read json --write yaml | bat --language yaml
fi
)

@ -0,0 +1,17 @@
make_script() (
for FILE in "$@"; do
cat <<EOF >"${FILE}"
#!/usr/bin/env bash
set -euo pipefail
main() (
echo "todo"
exit 1
)
main "\$@"
EOF
chmod +x "${FILE}"
done
)

@ -0,0 +1,13 @@
require assert/command
assert_command jpegtran
min_jpg() (
for FILE in "$@"; do
PERMISSIONS=$(stat --format '%a' "${FILE}")
OUT_FILE=$(mktemp)
jpegtran -optimize -perfect -outfile "${OUT_FILE}" "${FILE}"
mv "${OUT_FILE}" "${FILE}"
chmod "${PERMISSIONS}" "${FILE}"
done
)

@ -0,0 +1,13 @@
require assert/command
assert_command pngcrush
min_png() (
for FILE in "$@"; do
PERMISSIONS=$(stat --format '%a' "${FILE}")
OUT_FILE=$(mktemp)
pngcrush -rem alla -reduce -brute "${FILE}" "${OUT_FILE}"
mv "${OUT_FILE}" "${FILE}"
chmod "${PERMISSIONS}" "${FILE}"
done
)

@ -0,0 +1,8 @@
mkdir_if_missing() (
dir=${1}
sudo=${2-}
if [ ! -d "${dir}" ]; then
# shellcheck disable=SC2086
$sudo mkdir -p "${dir}"
fi
)

@ -0,0 +1,4 @@
node_current() (
curl --silent https://nodejs.org/dist/index.tab |
cut -f 1,10 | grep '-' | head -n 1 | cut -f 1
)

@ -0,0 +1,4 @@
node_lts() (
curl --silent https://nodejs.org/dist/index.tab |
cut -f 1,10 | grep -v '-' | head -n 2 | tail -n 1 | cut -f 1
)

@ -0,0 +1,43 @@
relative_path() (
# both $1 and $2 are absolute paths beginning with /
# $1 must be a canonical path; that is none of its directory
# components may be ".", ".." or a symbolic link
#
# returns relative path to $2/$target from $1/$src
src=$1
target=$2
common_part=$src
result=
while [ "${target#"$common_part"}" = "$target" ]; do
# no match, means that candidate common part is not correct
# go up one level (reduce common part)
common_part=$(dirname "$common_part")
# and record that we went back, with correct / handling
if [ -z "$result" ]; then
result=..
else
result=../$result
fi
done
if [ "$common_part" = / ]; then
# special case for root (no common path)
result=$result/
fi
# since we now have identified the common part,
# compute the non-common part
forward_part=${target#"$common_part"}
# and now stick all parts together
if [ -n "$result" ] && [ -n "$forward_part" ]; then
result=$result$forward_part
elif [ -n "$forward_part" ]; then
# extra slash removal
result=${forward_part#?}
fi
printf '%s' "$result"
)

@ -0,0 +1,12 @@
#shellcheck disable=SC2120
slugify() (
if [ "$#" -ne 0 ]; then
#shellcheck disable=SC2119
echo "$@" | slugify
else
iconv --to-code ascii//TRANSLIT |
sed -E 's/[^a-zA-Z0-9]+/-/g' |
sed -E 's/^-+\|-+$//g' |
tr '[:upper:]' '[:lower:]'
fi
)

@ -0,0 +1,8 @@
unlink_if_set() (
dir=$1
sudo=${2-}
if [ -L "${dir}" ]; then
# shellcheck disable=SC2086
${sudo} unlink "${dir}"
fi
)

@ -0,0 +1,15 @@
require assert/command
assert_command bat dasel
yaml_to_json() (
ARG=${1-}
command_installed bat dasel
if [ -z "${ARG}" ]; then
dasel --read yaml --write json | bat --language json
elif [ -f "${ARG}" ]; then
dasel --read yaml --write json --file "${ARG}" | bat --language json
else
echo "${ARG}" | dasel --read yaml --write json | bat --language json
fi
)
Loading…
Cancel
Save