first upload

This commit is contained in:
Frank Vitrano
2026-07-19 14:06:49 +08:00
committed by GitHub
parent 3d605622df
commit 7e611c3cd8
17 changed files with 1699 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
#!/usr/bin/env bash
# AUR RPC helpers (v5) using curl + jq
aur_rpc_get() {
local url=$1
local curl_bin
curl_bin=$(resolve_curl)
"$curl_bin" -fsSL --connect-timeout 15 --max-time 60 "$url"
}
# Search AUR; prints: name|version|votes|popularity|desc (one per line)
aur_search() {
local query=$1
local by=${2:-$MIAUR_SEARCHBY}
local encoded
encoded=$(printf '%s' "$query" | jq -sRr @uri)
local json
json=$(aur_rpc_get "${MIAUR_AURRPC}/search/${encoded}?by=${by}") || return 1
local sort=${MIAUR_SORTBY:-votes}
local jq_sort
case "$sort" in
votes) jq_sort='sort_by(.NumVotes) | reverse' ;;
popularity) jq_sort='sort_by(.Popularity) | reverse' ;;
name) jq_sort='sort_by(.Name)' ;;
*) jq_sort='sort_by(.NumVotes) | reverse' ;;
esac
jq -r --argjson err 0 "
if .type == \"error\" then empty
else (.results // []) | ${jq_sort} | .[] |
[.Name, .Version, (.NumVotes|tostring), (.Popularity|tostring), (.Description // \"\")] | join(\"|\")
end
" <<<"$json"
}
# Info for one package; prints JSON object or empty
aur_info_json() {
local name=$1
local encoded
encoded=$(printf '%s' "$name" | jq -sRr @uri)
local json
json=$(aur_rpc_get "${MIAUR_AURRPC}/info?arg[]=${encoded}") || return 1
jq -c '.results[0] // empty' <<<"$json"
}
# Multi-info: args are package names. Prints JSON array of results.
aur_multiinfo_json() {
local args=()
local n
for n in "$@"; do
args+=(--data-urlencode "arg[]=${n}")
done
[[ ${#args[@]} -eq 0 ]] && { echo '[]'; return 0; }
local json
local curl_bin
curl_bin=$(resolve_curl)
json=$("$curl_bin" -fsSL --connect-timeout 15 --max-time 60 -G "${MIAUR_AURRPC}/info" "${args[@]}") || return 1
jq -c '.results // []' <<<"$json"
}
# Check if package exists on AUR (exit 0 if yes)
aur_exists() {
local j
j=$(aur_info_json "$1") || return 1
[[ -n $j && $j != null ]]
}
# Print human-readable info for a package (like pacman -Si for AUR)
aur_print_info() {
local name=$1
local j
j=$(aur_info_json "$name") || return 1
[[ -z $j || $j == null ]] && return 1
jq -r '
"Repository : aur",
"Name : \(.Name)",
"Version : \(.Version)",
"Description : \(.Description // "")",
"URL : \(.URL // "")",
"AUR URL : https://aur.archlinux.org/packages/\(.Name)",
"Groups : \((.Groups // []) | join(" "))",
"Depends On : \((.Depends // []) | join(" "))",
"Make Depends : \((.MakeDepends // []) | join(" "))",
"Check Depends : \((.CheckDepends // []) | join(" "))",
"Optional Deps : \((.OptDepends // []) | join(" "))",
"Provides : \((.Provides // []) | join(" "))",
"Conflicts With : \((.Conflicts // []) | join(" "))",
"Replaces : \((.Replaces // []) | join(" "))",
"Maintainer : \(.Maintainer // "(orphan)")",
"Votes : \(.NumVotes)",
"Popularity : \(.Popularity)",
"Last Modified : \(.LastModified)",
"Out Of Date : \(if .OutOfDate then (.OutOfDate|tostring) else "No" end)"
' <<<"$j"
}
# Print search line for AUR result: aur/name version (votes) desc
aur_format_search_line() {
local name=$1 ver=$2 votes=$3 pop=$4 desc=$5
local installed=
if is_pkg_installed "$name"; then
local localver
localver=$(pkg_local_version "$name")
if [[ $localver == "$ver" ]]; then
installed=" ${C_GREEN}[installed]${C_RESET}"
else
installed=" ${C_YELLOW}[installed: ${localver}]${C_RESET}"
fi
fi
printf '%saur/%s%s %s%s (%s)%s\n %s\n' \
"${C_BOLD}${C_CYAN}" "$name" "${C_RESET}" \
"${C_BOLD}${C_GREEN}${ver}${C_RESET}" \
"$installed" \
"$votes" \
"" \
"$desc"
}
+201
View File
@@ -0,0 +1,201 @@
#!/usr/bin/env bash
# Build and install AUR packages
# Build package in dir. install=1 to install after build.
# asdeps=1 to mark as dependency.
build_pkg() {
local dir=$1
local do_install=${2:-1}
local asdeps=${3:-0}
local name
name=$(basename "$dir")
msg "Building $name..."
(
cd "$dir" || exit 1
# Clear previous package files to avoid installing stale ones
rm -f ./*.pkg.tar.* 2>/dev/null || true
local -a mflags=()
# shellcheck disable=SC2206
mflags=(${MIAUR_MFLAGS})
# If MFLAGS contains -si we handle install ourselves more carefully
local -a clean_flags=()
local f
for f in "${mflags[@]}"; do
case "$f" in
-i | --install | -si | -is) ;;
*) clean_flags+=("$f") ;;
esac
done
# Always sync deps for repo side via makepkg -s when needed
${MIAUR_MAKEPKG} -s --noconfirm "${clean_flags[@]}" || exit 1
) || die "build failed: $name"
if [[ $do_install -eq 1 ]]; then
local -a pkgs=()
mapfile -t pkgs < <(find "$dir" -maxdepth 1 -type f \( -name '*.pkg.tar.zst' -o -name '*.pkg.tar.xz' -o -name '*.pkg.tar.gz' \) ! -name '*-debug-*' | sort)
[[ ${#pkgs[@]} -eq 0 ]] && die "no build artifacts found: $name"
msg "Installing $name..."
local -a uflags=(-U --noconfirm)
[[ $asdeps -eq 1 ]] && uflags+=(--asdeps)
as_root ${MIAUR_PACMAN} "${uflags[@]}" "${pkgs[@]}" || die "installation failed: $name"
fi
if [[ ${MIAUR_CLEANAFTER:-0} -eq 1 ]]; then
msg2 "Cleaning build directory: $dir"
rm -rf "$dir"
fi
}
# Full install pipeline for AUR package names (and their AUR deps)
# Targets are package names.
install_aur_packages() {
local -a targets=("$@")
[[ ${#targets[@]} -eq 0 ]] && return 0
need_cmd curl jq git makepkg pacman
sudoloop_start
msg "Resolving dependencies..."
local -a order=()
mapfile -t order < <(aur_resolve_order "${targets[@]}")
[[ ${#order[@]} -eq 0 ]] && die "no packages to install"
# Collect all repo deps across the graph
local -a repo_deps=()
local pkg dir line
declare -A repo_seen=()
for pkg in "${order[@]}"; do
dir=$(pkg_clone_dir "$pkg")
[[ -d $dir ]] || dir=$(aur_clone "$pkg")
while IFS= read -r line; do
case "$line" in
repo\|*)
local rn=${line#repo|}
[[ -n ${repo_seen[$rn]+x} ]] && continue
repo_seen[$rn]=1
repo_deps+=("$rn")
;;
esac
done < <(collect_deps_for_dir "$dir" 1 1)
done
install_repo_deps "${repo_deps[@]}"
# Review all packages first (like paru)
local -a build_list=()
for pkg in "${order[@]}"; do
dir=$(pkg_clone_dir "$pkg")
[[ -d $dir ]] || dir=$(aur_clone "$pkg")
local rc=0
review_pkgbuild "$dir" "$pkg" || rc=$?
if [[ $rc -eq 1 ]]; then
die "installation cancelled"
elif [[ $rc -eq 2 ]]; then
warn "Skipping $pkg"
continue
fi
build_list+=("$pkg")
done
[[ ${#build_list[@]} -eq 0 ]] && die "no packages to build"
# Explicit install targets (deps marked --asdeps)
declare -A is_target=()
for pkg in "${targets[@]}"; do
is_target[$pkg]=1
done
# Track make deps for optional removal (names from srcinfo)
local -a make_pkgs=()
declare -A make_seen=()
if [[ ${MIAUR_REMOVEMAKE:-0} -eq 1 ]]; then
for pkg in "${build_list[@]}"; do
dir=$(pkg_clone_dir "$pkg")
while IFS= read -r line; do
case "$line" in
makedepend\|*)
local mn
mn=$(dep_name "${line#makedepend|}")
[[ -n ${is_target[$mn]+x} ]] && continue
[[ -n ${make_seen[$mn]+x} ]] && continue
make_seen[$mn]=1
make_pkgs+=("$mn")
;;
esac
done < <(srcinfo_dump "$dir")
done
fi
for pkg in "${build_list[@]}"; do
dir=$(pkg_clone_dir "$pkg")
local asdeps=0
[[ -z ${is_target[$pkg]+x} ]] && asdeps=1
# Skip if already up to date
if is_pkg_installed "$pkg"; then
local localver aurver
localver=$(pkg_local_version "$pkg")
aurver=$(jq -r '.Version // empty' <<<"$(aur_info_json "$pkg")" 2>/dev/null || true)
if [[ -n $aurver && $localver == "$aurver" && -z ${is_target[$pkg]+x} ]]; then
msg2 "$pkg is up to date ($localver)"
continue
fi
fi
build_pkg "$dir" 1 "$asdeps"
done
# Remove make depends that were installed as deps and not needed
if [[ ${MIAUR_REMOVEMAKE:-0} -eq 1 && ${#make_pkgs[@]} -gt 0 ]]; then
local -a to_rm=()
local m
for m in "${make_pkgs[@]}"; do
# Only remove if installed as dependency and not required
if pacman -Qdtq 2>/dev/null | grep -qx "$m"; then
to_rm+=("$m")
fi
done
if [[ ${#to_rm[@]} -gt 0 ]]; then
msg "Removing make dependencies: ${to_rm[*]}"
as_root ${MIAUR_PACMAN} -Rns --noconfirm "${to_rm[@]}" || warn "failed to remove make dependencies"
fi
fi
sudoloop_stop
msg "Done"
}
# Build from local directories containing PKGBUILD
build_local_dirs() {
local do_install=${1:-0}
shift
local -a dirs=("$@")
local d
for d in "${dirs[@]}"; do
[[ -d $d ]] || die "directory does not exist: $d"
[[ -f $d/PKGBUILD ]] || die "PKGBUILD not found: $d"
# Resolve AUR deps of local pkgbuild
local -a aur_deps=()
local line
while IFS= read -r line; do
case "$line" in
aur\|*) aur_deps+=("${line#aur|}") ;;
missing\|*) die "unable to resolve dependency: ${line#missing|}" ;;
repo\|*) ;;
esac
done < <(collect_deps_for_dir "$d" 1 1)
if [[ ${#aur_deps[@]} -gt 0 ]]; then
install_aur_packages "${aur_deps[@]}"
fi
# Install repo deps
local -a rd=()
while IFS= read -r line; do
case "$line" in
repo\|*) rd+=("${line#repo|}") ;;
esac
done < <(collect_deps_for_dir "$d" 1 1)
install_repo_deps "${rd[@]}"
review_pkgbuild "$d" "$(basename "$d")" || die "cancelled"
build_pkg "$d" "$do_install" 0
done
}
+202
View File
@@ -0,0 +1,202 @@
#!/usr/bin/env bash
# CLI parsing — paru-like short option clusters
# Globals set by parse_args
OP= # S Q R G B P D F T U none interactive
OP_REFRESH=0 # y
OP_UPGRADE=0 # u
OP_SEARCH=0 # s (count)
OP_INFO=0 # i
OP_CLEAN=0 # c
OP_AUR_ONLY=0 # a (with S/Q)
OP_PRINT=0 # -p for -G
OP_BUILD_INSTALL=0 # -i for -B
TARGETS=()
PACMAN_EXTRA=() # leftover short flags for pacman passthrough
DO_PASSTHRU=0
print_help() {
cat <<'EOF'
Usage:
miaur
miaur <operation> [...]
miaur <search terms>
miaur is a pure-shell AUR helper with paru-like everyday commands.
Pacman operations extended with AUR support:
miaur {-h --help}
miaur {-V --version}
miaur {-Q --query} [options] [packages]
miaur {-R --remove} [options] <packages>
miaur {-S --sync} [options] [packages]
miaur {-U --upgrade} [options] <files>
Additional operations:
miaur {-G --getpkgbuild} [packages]
miaur {-B --build} [directories]
With no arguments, miaur runs 'miaur -Syu'.
Common commands:
miaur -Syu Upgrade repository and AUR packages
miaur -Sua Upgrade AUR packages only
miaur -S <package> Install from a repository or the AUR
miaur -Ss <query> Search packages
miaur -Si <package> Show package information
miaur -Qua List available AUR upgrades
miaur -G <package> Download a PKGBUILD
miaur -Gp <package> Print a PKGBUILD
miaur -Bi . Build and install a local PKGBUILD
Options:
-a, --aur Assume targets are from the AUR
--repo Assume targets are from repositories
--skipreview Skip PKGBUILD review
--cleanafter Remove build sources after installation
--[no]removemake Remove make dependencies after installation
--bottomup Show AUR results first
--topdown Show repository results first (default)
--clonedir <path> Set the PKGBUILD clone directory
--mflags <flags> Pass additional flags to makepkg
--sudoloop Keep sudo credentials alive (default)
--nosudoloop Disable the sudo keep-alive loop
EOF
}
parse_args() {
if [[ $# -eq 0 ]]; then
OP=S
OP_REFRESH=1
OP_UPGRADE=1
return 0
fi
# No leading dash → interactive search
if [[ $1 != -* ]]; then
OP=interactive
TARGETS=("$@")
return 0
fi
local -a raw=("$@")
local i=0
while ((i < ${#raw[@]})); do
local arg=${raw[$i]}
case "$arg" in
-h | --help)
print_help
exit 0
;;
-V | --version)
printf 'miaur v%s\n' "$MIAUR_VERSION"
exit 0
;;
--aur)
OP_AUR_ONLY=1
MIAUR_MODE=aur
;;
--repo)
MIAUR_MODE=repo
;;
--skipreview) MIAUR_SKIPREVIEW=1 ;;
--review) MIAUR_SKIPREVIEW=0 ;;
--cleanafter) MIAUR_CLEANAFTER=1 ;;
--nocleanafter) MIAUR_CLEANAFTER=0 ;;
--removemake) MIAUR_REMOVEMAKE=1 ;;
--noremovermake) MIAUR_REMOVEMAKE=0 ;;
--bottomup) MIAUR_BOTTOMUP=1 ;;
--topdown) MIAUR_BOTTOMUP=0 ;;
--sudoloop) MIAUR_SUDOLOOP=1 ;;
--nosudoloop) MIAUR_SUDOLOOP=0 ;;
--clonedir)
i=$((i + 1))
MIAUR_CLONEDIR=${raw[$i]:-}
[[ -n $MIAUR_CLONEDIR ]] || die "--clonedir requires a path"
;;
--clonedir=*) MIAUR_CLONEDIR=${arg#*=} ;;
--mflags)
i=$((i + 1))
MIAUR_MFLAGS=${raw[$i]:-}
;;
--mflags=*) MIAUR_MFLAGS=${arg#*=} ;;
--mode)
i=$((i + 1))
MIAUR_MODE=${raw[$i]:-}
;;
--mode=*) MIAUR_MODE=${arg#*=} ;;
--sync) OP=S ;;
--query) OP=Q ;;
--remove)
OP=R
DO_PASSTHRU=1
;;
--getpkgbuild) OP=G ;;
--build) OP=B ;;
--database | --files | --deptest | --upgrade)
DO_PASSTHRU=1
;;
--)
i=$((i + 1))
while ((i < ${#raw[@]})); do
TARGETS+=("${raw[$i]}")
i=$((i + 1))
done
break
;;
-*)
local cluster=${arg:1}
local j c
for ((j = 0; j < ${#cluster}; j++)); do
c=${cluster:j:1}
case "$c" in
S) OP=S ;;
Q) OP=Q ;;
R)
OP=R
DO_PASSTHRU=1
;;
G) OP=G ;;
B) OP=B ;;
D | F | T | U) DO_PASSTHRU=1 ;;
y) OP_REFRESH=1 ;;
u) OP_UPGRADE=1 ;;
s) OP_SEARCH=$((OP_SEARCH + 1)) ;;
i)
if [[ $OP == B ]]; then
OP_BUILD_INSTALL=1
else
OP_INFO=1
fi
;;
c) OP_CLEAN=1 ;;
a)
OP_AUR_ONLY=1
MIAUR_MODE=aur
;;
p)
if [[ $OP == G ]]; then
OP_PRINT=1
else
PACMAN_EXTRA+=("$c")
fi
;;
h)
print_help
exit 0
;;
V)
printf 'miaur v%s\n' "$MIAUR_VERSION"
exit 0
;;
*) PACMAN_EXTRA+=("$c") ;;
esac
done
;;
*)
TARGETS+=("$arg")
;;
esac
i=$((i + 1))
done
}
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# Clone / update AUR package sources
pkg_clone_dir() {
local name=$1
printf '%s/%s' "${MIAUR_CLONEDIR}" "$name"
}
# Clone or pull package; prints absolute path to clone dir
aur_clone() {
local name=$1
local dir
dir=$(pkg_clone_dir "$name")
mkdir -p "$MIAUR_CLONEDIR"
if [[ -d $dir/.git ]]; then
msg2 "Updating $name..."
if ! (cd "$dir" && ${MIAUR_GIT} pull --ff-only --quiet); then
warn "Cannot fast-forward $name; cloning it again"
rm -rf "$dir"
${MIAUR_GIT} clone --quiet "${MIAUR_AURURL}/${name}.git" "$dir" || die "clone failed: $name"
fi
else
rm -rf "$dir"
msg2 "Cloning $name..."
${MIAUR_GIT} clone --quiet "${MIAUR_AURURL}/${name}.git" "$dir" || die "clone failed: $name"
fi
[[ -f $dir/PKGBUILD ]] || die "PKGBUILD not found after cloning: $name"
printf '%s\n' "$dir"
}
# Clean clone cache for one package or all
aur_clean_cache() {
local target=${1:-}
if [[ -n $target ]]; then
local dir
dir=$(pkg_clone_dir "$target")
if [[ -d $dir ]]; then
msg "Cleaning cache: $dir"
rm -rf "$dir"
fi
else
if [[ -d $MIAUR_CLONEDIR ]]; then
msg "Cleaning AUR cache: $MIAUR_CLONEDIR"
# Keep VCS src dirs? Plan says clean clone cache — remove package dirs
find "$MIAUR_CLONEDIR" -mindepth 1 -maxdepth 1 -type d -exec rm -rf {} +
fi
fi
}
+108
View File
@@ -0,0 +1,108 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034
# Common utilities for miaur
MIAUR_VERSION="0.1.0"
# Colors (respect pacman Color / NO_COLOR / non-tty)
_miaur_color_enabled() {
[[ -n ${NO_COLOR:-} ]] && return 1
[[ ! -t 1 ]] && return 1
if [[ -f /etc/pacman.conf ]] && grep -Eq '^[[:space:]]*Color([[:space:]]|$)' /etc/pacman.conf 2>/dev/null; then
return 0
fi
# Default: enable on tty even if pacman Color is commented
return 0
}
if _miaur_color_enabled; then
C_RED=$'\033[31m'
C_GREEN=$'\033[32m'
C_YELLOW=$'\033[33m'
C_BLUE=$'\033[34m'
C_CYAN=$'\033[36m'
C_BOLD=$'\033[1m'
C_DIM=$'\033[2m'
C_RESET=$'\033[0m'
else
C_RED= C_GREEN= C_YELLOW= C_BLUE= C_CYAN= C_BOLD= C_DIM= C_RESET=
fi
msg() { printf '%s::%s %s\n' "${C_BOLD}${C_BLUE}" "${C_RESET}" "$*" >&2; }
msg2() { printf '%s ->%s %s\n' "${C_BOLD}${C_BLUE}" "${C_RESET}" "$*" >&2; }
warn() { printf '%s==>%s WARNING: %s\n' "${C_BOLD}${C_YELLOW}" "${C_RESET}" "$*" >&2; }
error() { printf '%s==>%s ERROR: %s\n' "${C_BOLD}${C_RED}" "${C_RESET}" "$*" >&2; }
die() { error "$*"; exit 1; }
need_cmd() {
local c
for c in "$@"; do
command -v "$c" >/dev/null 2>&1 || die "required command not found: $c"
done
}
# Prefer system curl — some user PATH wrappers break AUR HTTPS
resolve_curl() {
if [[ -n ${MIAUR_CURL:-} ]]; then
printf '%s' "$MIAUR_CURL"
elif [[ -x /usr/bin/curl ]]; then
printf '%s' /usr/bin/curl
else
command -v curl
fi
}
# Strip version constraints from a dep string: foo>=1.0 -> foo
dep_name() {
local d=$1
d=${d%%[><=]*}
d=${d%%:*}
printf '%s' "$d"
}
vercmp_safe() {
# pacman --print returns 0 if equal, 1 if first >, -1 if second >
/usr/bin/vercmp "$1" "$2"
}
is_pkg_installed() {
pacman -Q "$1" &>/dev/null
}
pkg_local_version() {
pacman -Q "$1" 2>/dev/null | awk '{print $2}'
}
# Run command as root via sudo
as_root() {
if [[ ${EUID:-$(id -u)} -eq 0 ]]; then
"$@"
else
${MIAUR_SUDO:-sudo} ${MIAUR_SUDOFLAGS:-} "$@"
fi
}
# Keep sudo credentials alive in background
_sudo_loop_pid=
sudoloop_start() {
[[ ${MIAUR_SUDOLOOP:-0} -eq 1 ]] || return 0
[[ ${EUID:-$(id -u)} -eq 0 ]] && return 0
${MIAUR_SUDO:-sudo} -v || return 1
(
while true; do
sleep 60
${MIAUR_SUDO:-sudo} -v || exit
done
) &
_sudo_loop_pid=$!
}
sudoloop_stop() {
if [[ -n ${_sudo_loop_pid:-} ]]; then
kill "$_sudo_loop_pid" 2>/dev/null || true
wait "$_sudo_loop_pid" 2>/dev/null || true
_sudo_loop_pid=
fi
}
trap sudoloop_stop EXIT
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
# Config defaults and loader for miaur
# Defaults
MIAUR_AURURL="${MIAUR_AURURL:-https://aur.archlinux.org}"
MIAUR_AURRPC="${MIAUR_AURRPC:-https://aur.archlinux.org/rpc/v5}"
MIAUR_CLONEDIR="${MIAUR_CLONEDIR:-${XDG_CACHE_HOME:-$HOME/.cache}/miaur}"
MIAUR_SKIPREVIEW="${MIAUR_SKIPREVIEW:-0}"
MIAUR_CLEANAFTER="${MIAUR_CLEANAFTER:-0}"
MIAUR_REMOVEMAKE="${MIAUR_REMOVEMAKE:-1}"
MIAUR_BOTTOMUP="${MIAUR_BOTTOMUP:-0}"
MIAUR_SUDOLOOP="${MIAUR_SUDOLOOP:-1}"
MIAUR_MFLAGS="${MIAUR_MFLAGS:--si}"
MIAUR_SORTBY="${MIAUR_SORTBY:-votes}"
MIAUR_SEARCHBY="${MIAUR_SEARCHBY:-name-desc}"
MIAUR_MODE="${MIAUR_MODE:-}" # aur|repo|empty=both
MIAUR_SUDO="${MIAUR_SUDO:-sudo}"
MIAUR_SUDOFLAGS="${MIAUR_SUDOFLAGS:-}"
MIAUR_GIT="${MIAUR_GIT:-git}"
MIAUR_MAKEPKG="${MIAUR_MAKEPKG:-makepkg}"
MIAUR_PACMAN="${MIAUR_PACMAN:-pacman}"
MIAUR_BAT="${MIAUR_BAT:-bat}"
MIAUR_EDITOR="${VISUAL:-${EDITOR:-nano}}"
_bool_val() {
case "${1,,}" in
1 | true | yes | on) printf '1' ;;
0 | false | no | off) printf '0' ;;
*) printf '%s' "$1" ;;
esac
}
miaur_load_config() {
local conf="${XDG_CONFIG_HOME:-$HOME/.config}/miaur/miaur.conf"
[[ -f $conf ]] || return 0
local line key val
while IFS= read -r line || [[ -n $line ]]; do
line=${line%%#*}
line=${line#"${line%%[![:space:]]*}"}
line=${line%"${line##*[![:space:]]}"}
[[ -z $line ]] && continue
key=${line%%=*}
val=${line#*=}
key=${key%"${key##*[![:space:]]}"}
key=${key#"${key%%[![:space:]]*}"}
val=${val#"${val%%[![:space:]]*}"}
val=${val%"${val##*[![:space:]]}"}
val=${val#\"}
val=${val%\"}
val=${val#\'}
val=${val%\'}
case "$key" in
AurUrl | AURURL) MIAUR_AURURL=$val ;;
AurRpcUrl | AURRPC) MIAUR_AURRPC=$val ;;
CloneDir)
MIAUR_CLONEDIR=$val
[[ $MIAUR_CLONEDIR == ~* ]] && MIAUR_CLONEDIR="${MIAUR_CLONEDIR/#\~/$HOME}"
;;
SkipReview) MIAUR_SKIPREVIEW=$(_bool_val "$val") ;;
CleanAfter) MIAUR_CLEANAFTER=$(_bool_val "$val") ;;
RemoveMake) MIAUR_REMOVEMAKE=$(_bool_val "$val") ;;
BottomUp) MIAUR_BOTTOMUP=$(_bool_val "$val") ;;
SudoLoop) MIAUR_SUDOLOOP=$(_bool_val "$val") ;;
MFlags) MIAUR_MFLAGS=$val ;;
SortBy) MIAUR_SORTBY=$val ;;
SearchBy) MIAUR_SEARCHBY=$val ;;
Sudo) MIAUR_SUDO=$val ;;
SudoFlags) MIAUR_SUDOFLAGS=$val ;;
Git) MIAUR_GIT=$val ;;
Makepkg) MIAUR_MAKEPKG=$val ;;
Pacman) MIAUR_PACMAN=$val ;;
Bat) MIAUR_BAT=$val ;;
Editor) MIAUR_EDITOR=$val ;;
esac
done <"$conf"
}
+156
View File
@@ -0,0 +1,156 @@
#!/usr/bin/env bash
# Dependency resolution from .SRCINFO / makepkg --printsrcinfo
# Parse srcinfo from directory into globals via stdout lines:
# pkgbase|NAME
# pkgname|NAME
# depend|DEP
# makedepend|DEP
# checkdepend|DEP
srcinfo_dump() {
local dir=$1
(
cd "$dir" || exit 1
${MIAUR_MAKEPKG} --printsrcinfo 2>/dev/null
) | awk '
/^pkgbase = / { print "pkgbase|" $3 }
/^[[:space:]]*pkgname = / { print "pkgname|" $3 }
/^[[:space:]]*depends = / { print "depend|" $3 }
/^[[:space:]]*makedepends = / { print "makedepend|" $3 }
/^[[:space:]]*checkdepends = / { print "checkdepend|" $3 }
'
}
# Returns 0 if dep is satisfied (installed or provided by installed pkg)
dep_satisfied() {
local dep name
name=$(dep_name "$1")
# pacman -T returns unsatisfied deps; empty means all ok
local out
out=$(pacman -T "$1" 2>/dev/null) || true
[[ -z $out ]]
}
# Is package available in sync repos?
repo_has_pkg() {
pacman -Si "$1" &>/dev/null
}
# Resolve whether a dep name is in AUR (not in repos, or --aur mode)
# Sets: REPO_DEPS[] AUR_DEPS[] (caller-managed arrays passed by name)
# resolve_one_dep name -> echoes "repo:name" or "aur:name" or "ok" or "missing:name"
resolve_one_dep() {
local raw=$1
local name
name=$(dep_name "$raw")
if dep_satisfied "$raw"; then
echo "ok"
return 0
fi
if [[ ${MIAUR_MODE:-} != aur ]] && repo_has_pkg "$name"; then
echo "repo:$name"
return 0
fi
if [[ ${MIAUR_MODE:-} != repo ]] && aur_exists "$name"; then
echo "aur:$name"
return 0
fi
# Try provides via pacman -Ssq exact? fallback AUR search by name
if [[ ${MIAUR_MODE:-} != repo ]] && aur_exists "$name"; then
echo "aur:$name"
return 0
fi
echo "missing:$name"
}
# Collect all deps for a cloned package dir into named arrays via stdout
# Format lines: repo|pkg / aur|pkg / missing|pkg
collect_deps_for_dir() {
local dir=$1
local include_make=${2:-1}
local include_check=${3:-1}
local line kind val
declare -A seen=()
while IFS= read -r line; do
kind=${line%%|*}
val=${line#*|}
case "$kind" in
depend) ;;
makedepend) [[ $include_make -eq 1 ]] || continue ;;
checkdepend) [[ $include_check -eq 1 ]] || continue ;;
*) continue ;;
esac
local name
name=$(dep_name "$val")
[[ -n ${seen[$name]+x} ]] && continue
seen[$name]=1
local r
r=$(resolve_one_dep "$val")
case "$r" in
ok) ;;
repo:*) echo "repo|${r#repo:}" ;;
aur:*) echo "aur|${r#aur:}" ;;
missing:*) echo "missing|${r#missing:}" ;;
esac
done < <(srcinfo_dump "$dir")
}
# Recursively expand AUR deps for a list of package names.
# Prints ordered AUR package names (dependencies first).
# Uses DFS + topo-ish order.
aur_resolve_order() {
local -a roots=("$@")
declare -A visiting=() visited=()
local -a order=()
_visit() {
local pkg=$1
[[ -n ${visited[$pkg]+x} ]] && return 0
if [[ -n ${visiting[$pkg]+x} ]]; then
warn "Dependency cycle detected at: $pkg"
return 0
fi
visiting[$pkg]=1
local dir
dir=$(aur_clone "$pkg") || die "failed to fetch dependency: $pkg"
local line
while IFS= read -r line; do
case "$line" in
aur\|*)
_visit "${line#aur|}"
;;
missing\|*)
die "unable to resolve dependency ${line#missing|} required by $pkg"
;;
esac
done < <(collect_deps_for_dir "$dir" 1 1)
unset 'visiting[$pkg]'
visited[$pkg]=1
order+=("$pkg")
}
local r
for r in "${roots[@]}"; do
_visit "$r"
done
printf '%s\n' "${order[@]}"
}
# Install repo packages as deps
install_repo_deps() {
local -a pkgs=("$@")
[[ ${#pkgs[@]} -eq 0 ]] && return 0
# Dedup
local -A u=()
local p
local -a list=()
for p in "${pkgs[@]}"; do
[[ -n ${u[$p]+x} ]] && continue
u[$p]=1
is_pkg_installed "$p" && continue
list+=("$p")
done
[[ ${#list[@]} -eq 0 ]] && return 0
msg "Installing repository dependencies: ${list[*]}"
as_root ${MIAUR_PACMAN} -S --needed --asdeps --noconfirm "${list[@]}"
}
+298
View File
@@ -0,0 +1,298 @@
#!/usr/bin/env bash
# Dispatch handlers for miaur operations
# Reconstruct pacman argv from OP + PACMAN_EXTRA + targets for passthrough
pacman_passthru() {
local -a args=("$@")
as_root ${MIAUR_PACMAN} "${args[@]}"
}
# Search repo + AUR
cmd_search() {
local -a terms=("$@")
[[ ${#terms[@]} -eq 0 ]] && die "specify a search query"
local query="${terms[*]}"
local -a repo_lines=() aur_lines=()
if [[ ${MIAUR_MODE:-} != aur ]]; then
mapfile -t repo_lines < <(${MIAUR_PACMAN} -Ss "$query" 2>/dev/null || true)
fi
if [[ ${MIAUR_MODE:-} != repo ]]; then
local line name ver votes pop desc
while IFS='|' read -r name ver votes pop desc; do
[[ -z $name ]] && continue
aur_lines+=("$(aur_format_search_line "$name" "$ver" "$votes" "$pop" "$desc")")
done < <(aur_search "$query" || true)
fi
if [[ ${MIAUR_BOTTOMUP:-0} -eq 1 ]]; then
local l
for l in "${aur_lines[@]}"; do printf '%b\n' "$l"; done
[[ ${#repo_lines[@]} -gt 0 && ${#aur_lines[@]} -gt 0 ]] && echo
local r
for r in "${repo_lines[@]}"; do printf '%s\n' "$r"; done
else
local r
for r in "${repo_lines[@]}"; do printf '%s\n' "$r"; done
[[ ${#repo_lines[@]} -gt 0 && ${#aur_lines[@]} -gt 0 ]] && echo
local l
for l in "${aur_lines[@]}"; do printf '%b\n' "$l"; done
fi
}
# Interactive search + install (like `paru foo`)
cmd_interactive() {
local query="$*"
[[ -z $query ]] && die "specify a search query"
local -a entries=() # type|name
local -a display=()
# Repo results
if [[ ${MIAUR_MODE:-} != aur ]]; then
local line repo name rest
while IFS= read -r line; do
[[ $line =~ ^([a-z0-9_-]+)/([^[:space:]]+) ]] || continue
repo=${BASH_REMATCH[1]}
name=${BASH_REMATCH[2]}
entries+=("repo|$name")
display+=("${C_BOLD}${C_BLUE}${repo}/${name}${C_RESET}")
done < <(${MIAUR_PACMAN} -Ss "$query" 2>/dev/null | grep -E '^[a-z0-9_-]+/' || true)
fi
# AUR results
if [[ ${MIAUR_MODE:-} != repo ]]; then
local name ver votes pop desc
while IFS='|' read -r name ver votes pop desc; do
[[ -z $name ]] && continue
entries+=("aur|$name")
display+=("${C_BOLD}${C_CYAN}aur/${name}${C_RESET} ${ver} (${votes})")
done < <(aur_search "$query" || true)
fi
[[ ${#entries[@]} -eq 0 ]] && die "no matches found: $query"
local i
if [[ ${MIAUR_BOTTOMUP:-0} -eq 1 ]]; then
for ((i = ${#display[@]} - 1; i >= 0; i--)); do
printf '%s%3d%s %b\n' "${C_DIM}" "$((i + 1))" "${C_RESET}" "${display[$i]}"
done
else
for ((i = 0; i < ${#display[@]}; i++)); do
printf '%s%3d%s %b\n' "${C_DIM}" "$((i + 1))" "${C_RESET}" "${display[$i]}"
done
fi
printf '%s::%s Select packages (for example: 1 3; leave empty to cancel): ' \
"${C_BOLD}${C_BLUE}" "${C_RESET}"
local sel=
read -r sel || exit 1
[[ -z $sel ]] && { msg "Cancelled"; return 0; }
local -a repo_pkgs=() aur_pkgs=()
local tok
for tok in $sel; do
if [[ $tok =~ ^[0-9]+$ ]]; then
local idx=$((tok - 1))
((idx >= 0 && idx < ${#entries[@]})) || die "invalid number: $tok"
local e=${entries[$idx]}
case "$e" in
repo\|*) repo_pkgs+=("${e#repo|}") ;;
aur\|*) aur_pkgs+=("${e#aur|}") ;;
esac
else
die "invalid selection: $tok"
fi
done
if [[ ${#repo_pkgs[@]} -gt 0 ]]; then
as_root ${MIAUR_PACMAN} -S --needed --noconfirm "${repo_pkgs[@]}"
fi
if [[ ${#aur_pkgs[@]} -gt 0 ]]; then
install_aur_packages "${aur_pkgs[@]}"
fi
}
cmd_info() {
local -a pkgs=("$@")
[[ ${#pkgs[@]} -eq 0 ]] && die "specify at least one package"
local p found
for p in "${pkgs[@]}"; do
found=0
if [[ ${MIAUR_MODE:-} != aur ]] && ${MIAUR_PACMAN} -Si "$p" 2>/dev/null; then
found=1
fi
if [[ ${MIAUR_MODE:-} != repo ]]; then
if aur_print_info "$p" 2>/dev/null; then
found=1
fi
fi
[[ $found -eq 1 ]] || error "package not found: $p"
done
}
cmd_sync_install() {
local -a pkgs=("$@")
[[ ${#pkgs[@]} -eq 0 ]] && die "specify at least one package"
local -a repo_pkgs=() aur_pkgs=()
local p
for p in "${pkgs[@]}"; do
if [[ ${MIAUR_MODE:-} == aur ]]; then
aur_pkgs+=("$p")
elif [[ ${MIAUR_MODE:-} == repo ]]; then
repo_pkgs+=("$p")
elif repo_has_pkg "$p"; then
repo_pkgs+=("$p")
elif aur_exists "$p"; then
aur_pkgs+=("$p")
else
die "package not found: $p"
fi
done
if [[ ${#repo_pkgs[@]} -gt 0 ]]; then
as_root ${MIAUR_PACMAN} -S --needed --noconfirm "${repo_pkgs[@]}"
fi
if [[ ${#aur_pkgs[@]} -gt 0 ]]; then
install_aur_packages "${aur_pkgs[@]}"
fi
}
cmd_getpkgbuild() {
local -a pkgs=("$@")
[[ ${#pkgs[@]} -eq 0 ]] && die "specify at least one package"
local p dir
for p in "${pkgs[@]}"; do
if [[ $OP_PRINT -eq 1 ]]; then
dir=$(aur_clone "$p")
cat "$dir/PKGBUILD"
else
dir=$(aur_clone "$p")
# Copy to cwd like paru -G
if [[ -e ./$p ]]; then
warn "./$p already exists; replacing it"
rm -rf "./$p"
fi
cp -a "$dir" "./$p"
msg "Downloaded to ./$p"
fi
done
}
cmd_clean() {
# -Sc: clean pacman cache + AUR clone cache
if [[ ${MIAUR_MODE:-} != aur ]]; then
as_root ${MIAUR_PACMAN} -Sc --noconfirm || true
fi
aur_clean_cache
}
dispatch() {
# Full passthrough ops (R, D, F, T, U, or unknown)
if [[ $DO_PASSTHRU -eq 1 && $OP != S && $OP != Q ]]; then
# Rebuild approximate pacman command from original? Simpler: use saved extras
local -a args=()
case "$OP" in
R) args+=(-R) ;;
*)
# Fall back: user should use pacman; try to forward
args+=(-"${OP}")
;;
esac
local c
for c in "${PACMAN_EXTRA[@]}"; do
args+=("-$c")
done
args+=("${TARGETS[@]}")
pacman_passthru "${args[@]}"
return $?
fi
case "$OP" in
interactive)
need_cmd curl jq
cmd_interactive "${TARGETS[@]}"
;;
S)
if [[ $OP_CLEAN -eq 1 ]]; then
cmd_clean
return
fi
if [[ $OP_SEARCH -gt 0 ]]; then
need_cmd curl jq
cmd_search "${TARGETS[@]}"
return
fi
if [[ $OP_INFO -eq 1 ]]; then
need_cmd curl jq
cmd_info "${TARGETS[@]}"
return
fi
if [[ $OP_UPGRADE -eq 1 ]]; then
need_cmd curl jq
if [[ $OP_AUR_ONLY -eq 1 ]]; then
# -Sua or -Su --aur
[[ $OP_REFRESH -eq 1 ]] && as_root ${MIAUR_PACMAN} -Sy --noconfirm
cmd_upgrade_aur
else
cmd_upgrade_all "$OP_REFRESH"
fi
return
fi
if [[ $OP_REFRESH -eq 1 && ${#TARGETS[@]} -eq 0 ]]; then
as_root ${MIAUR_PACMAN} -Sy --noconfirm
return
fi
# Install packages; optionally refresh first
[[ $OP_REFRESH -eq 1 ]] && as_root ${MIAUR_PACMAN} -Sy --noconfirm
need_cmd curl jq
cmd_sync_install "${TARGETS[@]}"
;;
Q)
if [[ $OP_UPGRADE -eq 1 && $OP_AUR_ONLY -eq 1 ]]; then
need_cmd curl jq
cmd_query_aur_upgrades
return
fi
if [[ $OP_UPGRADE -eq 1 ]]; then
# -Qu: repo + AUR
${MIAUR_PACMAN} -Qu || true
need_cmd curl jq
cmd_query_aur_upgrades || true
return
fi
# Default query passthrough
local -a qargs=(-Q)
local c
for c in "${PACMAN_EXTRA[@]}"; do
qargs+=("-$c")
done
[[ $OP_AUR_ONLY -eq 1 ]] && qargs+=(-m)
[[ $OP_SEARCH -gt 0 ]] && qargs+=(-s)
[[ $OP_INFO -eq 1 ]] && qargs+=(-i)
qargs+=("${TARGETS[@]}")
${MIAUR_PACMAN} "${qargs[@]}"
;;
G)
need_cmd curl jq git
cmd_getpkgbuild "${TARGETS[@]}"
;;
B)
[[ ${#TARGETS[@]} -eq 0 ]] && TARGETS=(.)
need_cmd git makepkg
build_local_dirs "$OP_BUILD_INSTALL" "${TARGETS[@]}"
;;
"")
if [[ $DO_PASSTHRU -eq 1 ]]; then
die "unable to parse operation; use pacman directly or see --help"
fi
die "no operation specified"
;;
*)
die "unsupported operation: $OP"
;;
esac
}
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
# PKGBUILD review
# Review PKGBUILD in dir. Returns 0 to continue, 1 to abort.
review_pkgbuild() {
local dir=$1
local name=${2:-$(basename "$dir")}
[[ ${MIAUR_SKIPREVIEW:-0} -eq 1 ]] && return 0
local pb="$dir/PKGBUILD"
[[ -f $pb ]] || die "PKGBUILD not found: $dir"
msg "Reviewing the PKGBUILD for $name"
if command -v "${MIAUR_BAT}" >/dev/null 2>&1; then
"${MIAUR_BAT}" --style=plain --paging=always --language=bash "$pb" || true
elif command -v less >/dev/null 2>&1; then
less -R "$pb" || true
else
cat "$pb"
fi
# Also show install script / other files briefly
local f
for f in "$dir"/*.install "$dir"/PKGBUILD; do
[[ -f $f ]] || continue
done
while true; do
printf '%s::%s View diff (d), edit (e), continue (y), skip (s), or quit (q)? [Y/d/e/s/q] ' \
"${C_BOLD}${C_BLUE}" "${C_RESET}"
local ans=
read -r ans || return 1
ans=${ans:-y}
case "${ans,,}" in
y | yes | '') return 0 ;;
q | quit | n | no)
warn "Cancelled by user"
return 1
;;
s | skip) return 2 ;;
e | edit)
"${MIAUR_EDITOR}" "$pb"
;;
d | diff)
if [[ -d $dir/.git ]]; then
(cd "$dir" && ${MIAUR_GIT} diff -- PKGBUILD) || true
else
warn "No Git history is available for comparison"
fi
;;
*)
warn "Invalid input"
;;
esac
done
}
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env bash
# AUR upgrade detection and application
# List foreign packages (AUR candidates): name version
foreign_packages() {
pacman -Qm
}
# Compare installed foreign pkgs with AUR; print upgradable: name local -> remote
aur_outdated() {
local -a names=()
local -A localver=()
local name ver
while read -r name ver; do
[[ -z $name ]] && continue
names+=("$name")
localver[$name]=$ver
done < <(foreign_packages)
[[ ${#names[@]} -eq 0 ]] && return 0
# Batch in chunks of 100
local i chunk=100
local -a batch=()
for ((i = 0; i < ${#names[@]}; i++)); do
batch+=("${names[$i]}")
if ((${#batch[@]} == chunk || i == ${#names[@]} - 1)); then
local json
json=$(aur_multiinfo_json "${batch[@]}") || {
warn "Failed to query AUR updates"
batch=()
continue
}
local line rname rver
while IFS= read -r line; do
rname=${line%%|*}
rver=${line#*|}
[[ -z $rname ]] && continue
local lv=${localver[$rname]:-}
[[ -z $lv ]] && continue
if [[ $(vercmp_safe "$rver" "$lv") -gt 0 ]]; then
printf '%s %s -> %s\n' "$rname" "$lv" "$rver"
fi
done < <(jq -r '.[] | "\(.Name)|\(.Version)"' <<<"$json")
batch=()
fi
done
}
# Print outdated like pacman -Qu
cmd_query_aur_upgrades() {
local name old _arrow new
while read -r name old _arrow new; do
[[ -z $name ]] && continue
printf '%s %s -> %s\n' "$name" "$old" "$new"
done < <(aur_outdated)
return 0
}
# Upgrade all outdated AUR packages
cmd_upgrade_aur() {
local -a pkgs=()
local name old arrow new
while read -r name old arrow new; do
[[ -z $name ]] && continue
pkgs+=("$name")
msg2 "$name ${old} -> ${new}"
done < <(aur_outdated)
if [[ ${#pkgs[@]} -eq 0 ]]; then
msg "No AUR package upgrades are available"
return 0
fi
msg "Upgrading ${#pkgs[@]} AUR package(s)"
install_aur_packages "${pkgs[@]}"
}
# Full -Syu: refresh + repo upgrade + AUR upgrade
cmd_upgrade_all() {
local refresh=${1:-0}
sudoloop_start
local -a pac_args=()
[[ $refresh -eq 1 ]] && pac_args+=(-y)
pac_args+=(-Su)
msg "Synchronizing databases and upgrading repository packages..."
# Use -Syu style via pacman
if [[ $refresh -eq 1 ]]; then
as_root ${MIAUR_PACMAN} -Syu --noconfirm || die "repository upgrade failed"
else
as_root ${MIAUR_PACMAN} -Su --noconfirm || die "repository upgrade failed"
fi
msg "Checking for AUR updates..."
cmd_upgrade_aur
sudoloop_stop
}