202 lines
9.1 KiB
Markdown
202 lines
9.1 KiB
Markdown
# sjaur
|
||
|
||
Fast, fuzzy **TUI search for the [Arch User Repository](https://aur.archlinux.org) and the official Arch Linux repositories** — with real package installation, built on the official AUR metadata dump and your local pacman sync databases.
|
||
|
||
```
|
||
┌ Search: firfox ────────────────────────────────────────────────────────┐
|
||
├──────────────────────────────────────┬─────────────────────────────────┤
|
||
│ firefox 130.0-1 +3500 │ Name firefox │
|
||
│ firefox-nightly 131.0b1 +120 │ Version 130.0-1 │
|
||
│ firefox-wayland 129.0 +90 │ Maintainer jonas │
|
||
│ … │ Votes 3500 │
|
||
│ │ Popularity 214.53 │
|
||
│ │ … │
|
||
├──────────────────────────────────────┴─────────────────────────────────┤
|
||
│ 9 results · 24 ms · cache 2.1 hours old │
|
||
└────────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
Typo-tolerant ranking across name, description, maintainer, keywords,
|
||
provides, and dependency fields — all served from a local snapshot, so
|
||
searching never touches the network.
|
||
|
||
## Install
|
||
|
||
```bash
|
||
cd sjaur
|
||
python -m venv .venv
|
||
.venv/bin/pip install .
|
||
```
|
||
|
||
Then link the executable onto your `PATH` (or copy it):
|
||
|
||
```bash
|
||
sudo ln -s "$(pwd)/.venv/bin/sjaur" /usr/local/bin/sjaur
|
||
# or, without root:
|
||
mkdir -p ~/.local/bin && ln -s "$(pwd)/.venv/bin/sjaur" ~/.local/bin/sjaur
|
||
```
|
||
|
||
Requirements: Python ≥ 3.10 and Linux (deps: `rapidfuzz`, `textual`).
|
||
|
||
## Usage
|
||
|
||
```bash
|
||
sjaur # launch the TUI
|
||
sjaur --update # force a metadata refresh, then exit
|
||
sjaur --version
|
||
sjaur --help
|
||
```
|
||
|
||
Optional:
|
||
|
||
- `--cache-dir <dir>` overrides the cache location (default `~/.cache/sjaur`)
|
||
- `--no-repos` searches the AUR only (skip official repository packages)
|
||
- `--repos-dir <dir>` overrides the pacman sync database directory
|
||
(default `/var/lib/pacman/sync`)
|
||
|
||
## Official repository packages
|
||
|
||
By default, sjaur merges the packages of your **local pacman sync databases**
|
||
(`core`, `extra`, …) into the search index. They appear with a repository
|
||
badge (`firefox [extra]`), can be installed directly via
|
||
`sudo pacman -S <name>` (`i` in the TUI — no build step, pacman asks for
|
||
confirmation), and work with all search features including `@maintainer`
|
||
(the packager email is used, e.g. `@heftig`). Votes, popularity, and
|
||
out-of-date status are AUR-only concepts and are hidden for repo packages.
|
||
|
||
The databases are read-only and stay where pacman manages them; run
|
||
`pacman -Sy` as usual to refresh them. On systems without sync databases,
|
||
sjaur falls back to AUR-only search with a warning.
|
||
|
||
## Search syntax
|
||
|
||
| Query | Meaning |
|
||
| --------------------------- | ---------------------------------------------------------------- |
|
||
| `firefox` | fuzzy search across all package fields |
|
||
| `@max_musterman` | fuzzy-search the **maintainer** field |
|
||
| `@max_musterman firefox` | maintainer search combined with a normal search |
|
||
| `@john python editor` | maintainer query is `john`; `python editor` is the normal query |
|
||
|
||
Rules:
|
||
|
||
- Only the **first whitespace-delimited word** after `@` is the maintainer
|
||
query; everything after it is the normal search.
|
||
- Matching stays fuzzy: `@max_musterman` also finds `max_mustermann`,
|
||
`max-musterman` or `max_musterman123` — no exact match required.
|
||
- With `@`, the result score is
|
||
`maintainer_similarity × 0.65 + package_score × 0.35`, so maintainer matches
|
||
dominate while the text query still orders the results.
|
||
- The parser is table-driven, so future operators (e.g. `#keyword`,
|
||
`depends:python`) can be added without touching the search engine.
|
||
|
||
### Live search
|
||
|
||
Results update **while you type** — no Enter needed:
|
||
|
||
1. On every keystroke the current list is instantly narrowed client-side
|
||
(packages that no longer match disappear immediately).
|
||
2. After a short pause (0.18 s), the full fuzzy search over all fields runs
|
||
in the background and refines the list.
|
||
|
||
Editing or retyping a recent query restores its result set instantly from an
|
||
in-memory cache; the status line shows `live` while you are typing and the
|
||
measured search time once the background search has finished.
|
||
|
||
## Keys
|
||
|
||
| Key | Action |
|
||
| ---------------------- | ----------------------------------------- |
|
||
| typing | **live search** — results update on every keystroke |
|
||
| `↑` / `↓` | navigate results |
|
||
| `enter` | open package details (`esc` returns) |
|
||
| `i` / `ctrl+i` | install the selected package |
|
||
| `tab` / `shift+tab` | switch between search field and results |
|
||
| `page up` / `page down`, `home` / `end` | scroll / jump |
|
||
| `ctrl+r` | reload AUR metadata now |
|
||
| `f1` | help overlay |
|
||
| `esc` | clear search / leave current view |
|
||
| `ctrl+c` | quit |
|
||
|
||
## Installing packages
|
||
|
||
With a package highlighted, press `i` (or `ctrl+i`) and confirm with `y`.
|
||
sjaur then runs the classic AUR workflow with plain system tools:
|
||
|
||
1. **Clone** the package repository from
|
||
`https://aur.archlinux.org/<name>.git` into `~/.cache/sjaur/build/<name>`
|
||
(an existing clone is **reused**; confirm with `u` instead of `y` to
|
||
hard-reset and update it first).
|
||
2. **Review** the PKGBUILD — it is printed to the terminal, and you can open
|
||
it in `$EDITOR` before deciding. **Always read it**: the AUR ships
|
||
user-produced build scripts.
|
||
3. **Build & install** via `makepkg --syncdeps --install`. pacman's
|
||
confirmation, the sudo password prompt, and the whole build output appear
|
||
directly in your terminal; the TUI suspends and resumes afterwards.
|
||
|
||
The result is reported in the terminal; press Enter to return to sjaur.
|
||
If anything fails (clone error, dependency problem, aborted build), the
|
||
message explains what happened and the TUI keeps running.
|
||
|
||
> **Security note:** AUR packages are user-produced content. sjaur shows the
|
||
> PKGBUILD (and any bundled install scripts) before building — never install
|
||
> packages whose build steps you do not understand.
|
||
|
||
## Package details
|
||
|
||
Selecting a package shows everything the local snapshot knows — name,
|
||
version, description, maintainer, votes, popularity, last modified,
|
||
out-of-date status, upstream URL, AUR page, licenses, groups, keywords,
|
||
depends, makedepends, checkdepends, optdepends, provides, conflicts, replaces
|
||
— without any additional network request.
|
||
|
||
## Cache behavior
|
||
|
||
`sjaur` keeps a decompressed copy of the official extended metadata dump
|
||
(`https://aur.archlinux.org/packages-meta-ext-v1.json.gz`) in:
|
||
|
||
```
|
||
~/.cache/sjaur/packages-meta-ext-v1.json
|
||
```
|
||
|
||
- At startup the cache's **modification time** is checked; if it is younger
|
||
than **24 hours** it is used as-is, otherwise a fresh dump is downloaded.
|
||
- The replacement is **atomic**: the dump is downloaded to a temporary file,
|
||
decompressed, validated as JSON, and only then swapped in with
|
||
`os.replace`. A failed download never destroys a working cache.
|
||
- If the download fails but a (stale) cache exists, the old cache is used and
|
||
a warning is shown. If there is no usable cache at all, `sjaur` exits with
|
||
a clear error.
|
||
- `SJAUR_CACHE_DIR` overrides the cache directory (XDG `XDG_CACHE_HOME` is
|
||
respected).
|
||
|
||
## Development
|
||
|
||
```bash
|
||
python -m venv .venv
|
||
.venv/bin/pip install -e '.[dev]'
|
||
.venv/bin/pytest # run the test suite
|
||
.venv/bin/mypy src # type-check
|
||
```
|
||
|
||
Project layout:
|
||
|
||
```
|
||
sjaur/
|
||
├── src/sjaur/
|
||
│ ├── cli.py argument parsing and startup flow
|
||
│ ├── cache.py download / decompress / validate / atomic replace
|
||
│ ├── metadata.py typed Package model, tolerant dump parsing
|
||
│ ├── repodb.py local pacman sync database reader
|
||
│ ├── query.py extensible query parser (@maintainer, …)
|
||
│ ├── search.py fuzzy scoring and ranking engine
|
||
│ ├── installer.py clone / review / makepkg / pacman install workflow
|
||
│ ├── config.py paths and constants
|
||
│ ├── errors.py user-facing error types
|
||
│ └── tui/ Textual application, detail view
|
||
└── tests/
|
||
```
|
||
|
||
## License
|
||
|
||
MIT — see [LICENSE](LICENSE).
|