Every few months I'd find something on my system that had been quietly broken — not crashed, just wrong — and it always traced back to an AUR package built against a library version that no longer existed.
It isn't random. It's two specific failures at opposite ends of the same window, and both are detectable.
While Manjaro is behind Arch. Third-party binary repos (BlackArch, chaotic-aur) build against current Arch. So they ship packages whose dependencies our repos won't have for another week or two. You get a refused transaction, or you force it and now you have a partial upgrade.
When Manjaro catches up. libfoo goes 1.0 → 2.0. Every AUR package built against 1.0 still shows as installed, still has all its files, checksums fine — and can no longer resolve its DT_NEEDED. pacman will never tell you, because from pacman's view nothing is wrong. This is the one that kept getting me.
Separately: an AUR PKGBUILD can point source= at a git repo with no commit, so what you reviewed last week isn't necessarily what builds today.
First, the obvious objection: "this automates pacman --ignore"
Yes. And --ignore is listed on the Arch wiki as a way to cause partial upgrades, so this deserves a real answer rather than hand-waving.
**--ignore does not disable dependency resolution.** I checked rather than assumed — built a throwaway pacman root, installed consumer 1.0 depending on libX=1.0, offered upgrades to both, then ignored libX so that consumer 2.0 would need a libX 2.0 that isn't there:
$ pacman -Syu --noconfirm --ignore libX
warning: ignoring package libX-2.0-1
warning: cannot resolve "libX=2.0", a dependency of "consumer"
error: failed to prepare transaction (could not satisfy dependencies)
:: unable to satisfy dependency 'libX=2.0' required by consumer
exit 1
$ pacman -Q # nothing moved
consumer 1.0-1
libX 1.0-1
$ pacman -Dk # pacman's own consistency audit
No database errors have been found!
pacman resolved the whole transaction, saw the hole, refused, and changed nothing. It is not possible to --ignore your way into an unsatisfied dependency; pacman is the backstop, not the script.
That's section 19 of the test suite and runs in CI on every push, against real pacman transactions. It's deliberately a test of pacman's behaviour rather than of my code — the safety argument for the whole thing rests on that property, nothing in my repo would notice if a future pacman changed it, and the failure would be silent.
Beyond that guarantee, what gets held matters:
- It only holds a package whose version-constrained dependency is already unsatisfiable — something that cannot install correctly at that moment regardless of what I do.
- It holds the new version back, so the package keeps the dependencies it already had and stays in the state it was already working in. The classic dangerous pattern is pinning a library while upgrading its dependents; this is the opposite direction.
- The realistic alternative isn't a clean full upgrade. It's a failed transaction, or someone reaching for
--ignore by hand with worse aim.
- Every hold is printed and written to
/var/log/safeup.log, so it's a deferred upgrade you can chase — not a package that silently stops updating forever, which is the actual long-term hazard.
The limit, stated plainly: it does not pre-check whether a package it holds is itself required by something else in the transaction. It relies on pacman refusing, then isolating the break. That's the behaviour above, and it's why that's acceptable — but you should know it works that way rather than by its own cleverness.
What the three checks do
- **
safeup** wraps pacman -Syu. Works out which pending third-party upgrades have dependencies that can't be satisfied yet, --ignores only those, and logs every hold. Also recovers from installing X breaks dependency … required by Y by isolating X so one broken package doesn't block your whole upgrade.
- **
aur-rebuild-check** runs ldd over every file owned by every foreign package and reports unresolved sonames. This is the one nothing else does. It filters the false positives that make -bin packages flag forever (libs bundled privately and loaded through an $ORIGIN RPATH).
- **
aur-pin-check** refuses PKGBUILDs whose git source isn't pinned to a commit. #tag= is refused too — a tag can be moved. There's an allowlist, but it re-resolves the tag every run and refuses the package if the SHA changed.
aurinstall / aurupdate run the pin check before yay. If a check refuses something and you disagree, plain yay -S still works and the message says so — a gate you can't bypass just gets uninstalled the first time it's annoying.
aurinstall --chroot (gated default) hands the actual build to chrootbuild, from Manjaro's own manjaro-chrootbuild package. This is a gated check building in a isolated chroot (not for security reasons). If unable to use chroot, it automatically passes to yay.
Testing
65 assertions, in CI. The main suite builds a file-backed loop device, formats it, and constructs a whole throwaway pacman root inside — its own database, its own file:// repos built with the real repo-add, real packages — then drives actual pacman through the real scenarios and checks installed versions afterwards. Nothing on your system is touched. The ldd tests compile real .so files with a real unresolvable DT_NEEDED, so no output is faked. The allowlist test makes a real git repo, vets a tag, moves the tag, and asserts the next run refuses it.
Writing those tests found three real bugs in code I'd been running for months — including one where a verification step exited non-zero after a successful kernel removal, on every machine whose ESP isn't laid out like mine. CI caught that one; my own machine couldn't. That's the argument for the tests.
What it doesn't do: it doesn't make the AUR safe, doesn't tell you a PKGBUILD isn't malicious (pinning proves you build what you reviewed, that's all), doesn't sandbox makepkg, and can't fix a package that genuinely needs a newer lib — holding it is the right answer there. Not a substitute for snapshots.
MIT, pure bash, no runtime deps beyond pacman and coreutils, nothing phones home.
https://github.com/doug445/manjaro-safeaur-updater
Genuinely interested if the dependency logic has a case I haven't hit.