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
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 miaur contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+47
View File
@@ -1,2 +1,49 @@
# miaur # miaur
A lightweight AUR helper written in pure Bash. No helper compilation required. A lightweight AUR helper written in pure Bash. No helper compilation required.
Its everyday commands follow paru, including `-Syu`, `-S`, `-Ss`, `-Qua`, and `-G`.
## Install
```bash
git clone https://github.com/WHC2006/miaur.git
cd miaur
./install.sh
```
One command:
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/WHC2006/miaur/main/install.sh)
```
Install for the current user under `~/.local`:
```bash
./install.sh --user
```
The installer adds the required packages automatically: `base-devel`, `git`, `curl`, and `jq`.
## Usage
```bash
miaur # Upgrade everything (same as -Syu)
miaur foo # Search and select packages interactively
miaur -S <package> # Install a package
miaur -Ss <query> # Search packages
miaur -Qua # List available AUR upgrades
miaur -Sua # Upgrade AUR packages only
miaur -G <package> # Download a PKGBUILD
```
Run `miaur -h` for more options.
## Configuration
The optional configuration file is `~/.config/miaur/miaur.conf`. See `config/miaur.conf.example`.
## License
MIT
+28
View File
@@ -0,0 +1,28 @@
# bash completion for miaur
_miaur() {
local cur prev
COMPREPLY=()
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD - 1]}
local ops='-S -Syu -Sua -Ss -Si -Sc -Q -Qua -Qm -R -Rns -G -Gp -B -Bi -h -V'
local longs='--aur --repo --skipreview --cleanafter --removemake --noremovermake --bottomup --topdown --clonedir --mflags --sudoloop --nosudoloop --help --version'
case "$prev" in
-S | -Si | -Ss | -G | -Gp | -R | -Rns | -Qi)
COMPREPLY=($(compgen -W "$(pacman -Ssq "^${cur}" 2>/dev/null | head -50)" -- "$cur"))
return
;;
--clonedir)
COMPREPLY=($(compgen -d -- "$cur"))
return
;;
esac
if [[ $cur == -* ]]; then
COMPREPLY=($(compgen -W "$ops $longs" -- "$cur"))
else
COMPREPLY=($(compgen -W "$(pacman -Ssq "^${cur}" 2>/dev/null | head -40)" -- "$cur"))
fi
}
complete -F _miaur miaur
+27
View File
@@ -0,0 +1,27 @@
#compdef miaur
_miaur() {
local -a ops
ops=(
'-Syu:upgrade repository and AUR packages'
'-Sua:upgrade AUR packages only'
'-Ss:search packages'
'-Si:show package information'
'-S:install a package'
'-Qua:list AUR upgrades'
'-G:download a PKGBUILD'
'-Gp:print a PKGBUILD'
'-Bi:build and install'
'-Sc:clean caches'
'-Rns:remove a package'
'--aur:use the AUR only'
'--repo:use repositories only'
'--skipreview:skip PKGBUILD review'
'--bottomup:show AUR results first'
'--help:show help'
)
_arguments '*:package:_files' \
"${ops[@]}"
}
_miaur
+18
View File
@@ -0,0 +1,18 @@
# miaur configuration example
# Copy this file to ~/.config/miaur/miaur.conf
# AurUrl = https://aur.archlinux.org
# AurRpcUrl = https://aur.archlinux.org/rpc/v5
# CloneDir = ~/.cache/miaur
SkipReview = false
CleanAfter = false
RemoveMake = true
BottomUp = false
SudoLoop = true
# MFlags = -si
# SortBy = votes
# SearchBy = name-desc
# Editor = nvim
# Bat = bat
+139
View File
@@ -0,0 +1,139 @@
#!/usr/bin/env bash
# miaur one-click installer
# Usage:
# ./install.sh
# ./install.sh --user
# ./install.sh --uninstall
# bash <(curl -fsSL https://raw.githubusercontent.com/<user>/miaur/main/install.sh)
set -euo pipefail
# Override when forking: MIAUR_REPO=https://github.com/you/miaur.git ./install.sh
REPO_URL="${MIAUR_REPO:-https://github.com/WHC2006/miaur.git}"
BRANCH="${MIAUR_BRANCH:-main}"
USER_INSTALL=0
UNINSTALL=0
for arg in "$@"; do
case "$arg" in
--user) USER_INSTALL=1 ;;
--uninstall) UNINSTALL=1 ;;
-h | --help)
cat <<'EOF'
Usage: install.sh [--user] [--uninstall]
(default) Install system-wide to /usr/local (needs sudo)
--user Install to ~/.local (no sudo for the link)
--uninstall Remove installed miaur
EOF
exit 0
;;
*)
echo "Unknown option: $arg" >&2
exit 1
;;
esac
done
if [[ $USER_INSTALL -eq 1 ]]; then
PREFIX="${HOME}/.local"
SUDO=
else
PREFIX=/usr/local
SUDO=sudo
fi
SHARE_DIR="${PREFIX}/share/miaur"
BIN_LINK="${PREFIX}/bin/miaur"
die() { echo "error: $*" >&2; exit 1; }
need_root_ok() {
if [[ $USER_INSTALL -eq 0 && ${EUID:-$(id -u)} -ne 0 ]]; then
command -v sudo >/dev/null 2>&1 || die "sudo is required"
sudo -v || die "sudo authentication failed"
fi
}
uninstall() {
need_root_ok
echo "==> Uninstalling miaur"
$SUDO rm -f "$BIN_LINK"
$SUDO rm -rf "$SHARE_DIR"
echo "Done: removed $BIN_LINK and $SHARE_DIR"
}
install_deps() {
command -v pacman >/dev/null 2>&1 || die "Arch Linux is required (pacman not found)"
echo "==> Installing dependencies"
if [[ ${EUID:-$(id -u)} -eq 0 ]]; then
pacman -S --needed --noconfirm base-devel git curl jq
else
sudo pacman -S --needed --noconfirm base-devel git curl jq
fi
}
# Resolve source tree: current repo, or clone to temp
resolve_src() {
local here
here=$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)
if [[ -f $here/miaur && -d $here/lib ]]; then
SRC=$here
return 0
fi
echo "==> Cloning ${REPO_URL} (${BRANCH})"
TMP=$(mktemp -d)
trap 'rm -rf "${TMP:-}"' EXIT
git clone --depth 1 --branch "$BRANCH" "$REPO_URL" "$TMP/miaur" \
|| die "clone failed; set MIAUR_REPO to the repository URL"
SRC=$TMP/miaur
}
do_install() {
need_root_ok
install_deps
resolve_src
[[ -f $SRC/miaur && -d $SRC/lib ]] || die "incomplete source tree: $SRC"
echo "==> Installing to ${SHARE_DIR}"
$SUDO mkdir -p "$SHARE_DIR" "${PREFIX}/bin"
$SUDO rm -rf "$SHARE_DIR"
$SUDO mkdir -p "$SHARE_DIR"
$SUDO cp -a "$SRC/miaur" "$SRC/lib" "$SHARE_DIR/"
if [[ -d $SRC/completions ]]; then
$SUDO cp -a "$SRC/completions" "$SHARE_DIR/"
fi
if [[ -d $SRC/config ]]; then
$SUDO cp -a "$SRC/config" "$SHARE_DIR/"
fi
$SUDO chmod 755 "$SHARE_DIR/miaur"
$SUDO ln -sfn "$SHARE_DIR/miaur" "$BIN_LINK"
# Optional bash completion (system or user)
if [[ -d $SRC/completions ]]; then
if [[ $USER_INSTALL -eq 1 ]]; then
mkdir -p "${HOME}/.local/share/bash-completion/completions"
cp -f "$SRC/completions/miaur.bash" \
"${HOME}/.local/share/bash-completion/completions/miaur" 2>/dev/null || true
else
if [[ -d /usr/share/bash-completion/completions ]]; then
$SUDO cp -f "$SRC/completions/miaur.bash" \
/usr/share/bash-completion/completions/miaur 2>/dev/null || true
fi
fi
fi
echo
echo "Done: $(command -v miaur 2>/dev/null || echo "$BIN_LINK")"
"$BIN_LINK" -V
echo
echo "Try: miaur -Ss foo"
}
if [[ $UNINSTALL -eq 1 ]]; then
uninstall
else
do_install
fi
+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
}
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -euo pipefail
# miaur — pure-shell AUR helper (paru-like daily workflow)
MIAUR_HOME=$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)
# shellcheck source=lib/common.sh
source "$MIAUR_HOME/lib/common.sh"
# shellcheck source=lib/config.sh
source "$MIAUR_HOME/lib/config.sh"
# shellcheck source=lib/aur_rpc.sh
source "$MIAUR_HOME/lib/aur_rpc.sh"
# shellcheck source=lib/clone.sh
source "$MIAUR_HOME/lib/clone.sh"
# shellcheck source=lib/review.sh
source "$MIAUR_HOME/lib/review.sh"
# shellcheck source=lib/deps.sh
source "$MIAUR_HOME/lib/deps.sh"
# shellcheck source=lib/build.sh
source "$MIAUR_HOME/lib/build.sh"
# shellcheck source=lib/upgrade.sh
source "$MIAUR_HOME/lib/upgrade.sh"
# shellcheck source=lib/cli.sh
source "$MIAUR_HOME/lib/cli.sh"
# shellcheck source=lib/dispatch.sh
source "$MIAUR_HOME/lib/dispatch.sh"
main() {
miaur_load_config
# Fast path: operations that are pure pacman wrappers with no AUR flags
# Keep original argv for -R/-D/-F/-T/-U when no miaur-specific long opts mixed in awkwardly.
# We still parse to detect them.
local -a orig=("$@")
parse_args "$@"
# Special: if DO_PASSTHRU and OP empty, forward entire original command
if [[ $DO_PASSTHRU -eq 1 && -z ${OP:-} ]]; then
# Replace program name conceptually — strip miaur-only long options already consumed
as_root ${MIAUR_PACMAN} "${orig[@]}"
return $?
fi
# For -R with extras like -Rns: OP=R DO_PASSTHRU=1
if [[ ${OP:-} == R ]]; then
local -a rargs=(-R)
local c
for c in "${PACMAN_EXTRA[@]}"; do
rargs+=("-$c")
done
rargs+=("${TARGETS[@]}")
as_root ${MIAUR_PACMAN} "${rargs[@]}"
return $?
fi
dispatch
}
main "$@"