Das Problem
ansible-galaxy kann Collections installieren, hat aber keinen Befehl, um sie
zu entfernen. Collections können an mehreren Orten liegen: in den Pfaden aus
COLLECTIONS_PATHS (gesetzt in ansible.cfg oder
ANSIBLE_COLLECTIONS_PATH), in collections/-Verzeichnissen von Projekten und
in ansible_collections unter site-packages. Wer eine Collection entfernen
will, löscht ihr Verzeichnis mit rm -rf an jedem Ort, an dem eine Kopie
liegen könnte. Ob eine andere Collection noch davon abhängt, prüft dabei
niemand.
Der Bedarf ist belegt:
- ansible/ansible#67759, "Add ansible-galaxy collection uninstall", hat 179 Reaktionen (174 Daumen hoch) und 20 Kommentare und ist seit Februar 2020 offen. Nutzer berichten dort, dass sie zwei Versionen derselben Collection installiert hatten und nur wussten, wo eine davon liegt.
- Zwei Upstream-Pull-Requests wurden ohne Merge geschlossen: #73464 (2021, geschlossen 2022) und #84738 (2025, geschlossen im März 2026; er entfernte nur die angegebenen Collections, ohne Behandlung von Abhängigkeiten).
- Red Hat hat dazu einen Knowledge-Base-Artikel, "How to Uninstall an Ansible Collection".
ade uninstallaus ansible-dev-environment arbeitet nur auf den site-packages der virtuellen Umgebung, die es verwaltet, und prüft keine Abhängigkeiten.
Wie es funktioniert
galaxy-uninstall fragt Ansible,
wo Collections liegen: ansible-config dump liefert die konfigurierten
Suchpfade in ihrer Reihenfolge, ansible-galaxy collection list --format json
jede installierte Kopie. Ansible wird nicht importiert, ansible.cfg,
ANSIBLE_COLLECTIONS_PATH und -p gelten also so, wie Ansible selbst sie
liest. Daraus bestimmt das Werkzeug, welche Kopie Ansible lädt und welche von
einem früheren Pfad verdeckt werden. Vor dem Entfernen liest es die
Abhängigkeiten aus MANIFEST.json und bricht ab, wenn eine andere installierte
Collection davon abhängt und keine weitere Kopie installiert bleibt. --deps
entfernt zusätzlich Abhängigkeiten, die keine verbleibende Collection mehr
braucht, und --shadowed entfernt Kopien, die Ansible nie lädt.
Beim Entfernen einer Kopie werden ansible_collections/<ns>/<name>, das von
ansible-galaxy angelegte .info-Verzeichnis und, falls es danach leer ist,
das Namespace-Verzeichnis gelöscht. Collections, die zu einem pip-Paket
gehören, und Verzeichnisse mit einem .git-Checkout werden nicht gelöscht.
Bei Collections, die per Symlink eingebunden sind, wird nur der Link entfernt.
Das README zeigt einen echten Lauf (Pfade gekürzt) für ein Projekt mit
demo.app, das von demo.base abhängt, und zwei weiteren Kopien von
demo.base:
$ galaxy-uninstall --list
demo.app 1.0.0 ~/proj/collections/ansible_collections [active]
demo.base 1.0.0 ~/proj/collections/ansible_collections [active]
demo.base 2.0.0 ~/.ansible/collections/ansible_collections [shadowed by ~/proj/collections/ansible_collections]
demo.base 2.0.0 /venv/lib/python3.12/site-packages/ansible_collections [shadowed by ~/proj/collections/ansible_collections]
$ galaxy-uninstall demo.base --all
refusing: demo.app 1.0.0 in ~/proj/collections/ansible_collections depends on demo.base
remove the dependents too, or pass --force
$ galaxy-uninstall demo.app --deps -n
Will remove:
demo.app 1.0.0 ~/proj/collections/ansible_collections/demo/app
demo.base 1.0.0 ~/proj/collections/ansible_collections/demo/base
Afterwards Ansible loads demo.base 2.0.0 in ~/.ansible/collections/ansible_collections
Das Werkzeug ist eine Python-CLI, die nur die Standardbibliothek verwendet.
Tests
Die Unit-Tests legen Verzeichnisbäume mit Collections an und ersetzen die
Ansible-Befehle durch die Ausgabe, die diese liefern würden. Sie decken die
Suchreihenfolge und das Verdecken ab, die Auswahl einer Kopie bei mehreren
Installationen, die Abhängigkeitsprüfung, transitiv verwaiste Abhängigkeiten
für --deps, das Erkennen von pip-eigenen Collections und git-Checkouts, das
Entfernen der .info- und Namespace-Verzeichnisse, Symlinks, Probeläufe und
den Abbruch ohne -y, wenn stdin kein Terminal ist.
Der End-to-End-Test verwendet ein echtes ansible-core. Er baut demo.base
1.0.0 und 2.0.0 sowie demo.app mit ansible-galaxy, installiert sie offline
in zwei Pfade, entfernt Kopien mit galaxy-uninstall und prüft mit
ansible localhost, aus welcher Kopie ein Filter-Plugin geladen wird. Das CI
führt alle Tests mit ansible-core 2.15, 2.17 und dem neuesten Release (2.21)
aus. Dabei fiel ein Unterschied auf: ansible-core 2.15 und 2.17 brechen
ansible-galaxy collection list mit einem Fehler ab, wenn noch keiner der
Collection-Pfade existiert, während neuere Versionen eine leere Liste ausgeben.
Das Werkzeug behandelt diesen Fehler als leere Liste.
Grenzen
- Die Suchreihenfolge ist die, die
ansibleundansible-playbookaußerhalb eines Playbook-Verzeichnisses verwenden. Eincollections/-Verzeichnis neben einem Playbook wird nur berücksichtigt, wenn es mit-pangegeben wird. - Versionsbereiche in Abhängigkeiten werden nicht ausgewertet. Eine Abhängigkeit gilt als erfüllt, solange irgendeine Kopie installiert bleibt.
--depskann nicht wissen, welche Collections absichtlich installiert wurden, weil ansible-galaxy das nicht festhält.-nzeigt vorher den Plan, und--keepschützt eine Collection.- Rollen und
requirements.yml-Dateien werden nicht behandelt.
Code und Installationsanleitung liegen unter github.com/Jwrede/galaxy-uninstall.