Das Problem
yq liest YAML in einen Knotenbaum ein und
schreibt den Baum wieder aus. Der Parser behält weder Leerzeilen noch die
ursprüngliche Einrückung, deshalb formatiert yq -i die ganze Datei neu. Bei
von Hand gepflegten Dateien wie Kubernetes-Manifesten oder Helm-Values
erzeugt eine einzeilige Änderung einen großen Diff.
Die Anfragen sind seit Jahren offen:
- mikefarah/yq#515, "yq write strips completely blank lines from the output", hat 156 Reaktionen und 41 Kommentare und ist seit August 2020 offen.
- mikefarah/yq#465, "Preserve formatting with in place writing", hat 116 Reaktionen.
- mikefarah/yq#1359 hat 37 Reaktionen, mikefarah/yq#462 hat 26.
Der Maintainer hat die Ursache auf die YAML-Bibliothek zurückgeführt
(go-yaml/yaml#627); dort ist
eine Korrektur in Arbeit, aber nicht veröffentlicht. Die meistbewertete
Umgehung in #515 (61 Reaktionen) leitet die Ausgabe von yq durch
diff | patch. Spätere Kommentare im Thread berichten, dass sie in manchen
Fällen Kommentare verliert oder Listeneinträge zusammenführt. Formaterhaltende Editoren wie
yamlpath gibt es, sie verwenden aber
eine eigene Abfragesyntax, sodass vorhandene yq-Ausdrücke umgeschrieben
werden müssen.
Wie es funktioniert
yq-keep behält yq als Engine und nimmt
jeden Ausdruck an, der mit yq -i funktioniert. Für jede Datei erzeugt
yq '.' FILE die yq-Darstellung der unveränderten Datei und
yq EXPRESSION FILE die Darstellung nach dem Edit. Beide Ausgaben haben die
Formatierung von yq, also enthält der Zeilen-Diff zwischen ihnen nur den
Edit. Die Zeilen der ersten Darstellung werden der Originaldatei zugeordnet,
wobei Leerzeilen und Unterschiede im Leerraum ignoriert werden, und jeder
Hunk des Diffs wird an der zugeordneten Stelle im Original angewendet.
Eingefügte Zeilen werden so eingerückt wie ihr nächster Nachbar auf derselben
Ebene im Original.
Das Ergebnis wird erneut mit yq eingelesen und als JSON-Daten mit der Ausgabe
von yq EXPRESSION FILE verglichen. Weichen beide ab, bleibt die Datei
unverändert, und yq-keep endet mit Exit-Code 2. --diff zeigt die Änderung,
ohne sie zu schreiben.
Das ist die Ausgabe für eine kleine Kustomization-Datei mit einer Leerzeile zwischen den Abschnitten, einer mit vier Leerzeichen eingerückten Liste und drei Leerzeichen vor einem Kommentar:
$ yq-keep --diff '.resources += ["rg.yaml"] | .images[0].newTag = "1.3.0"' kustomization.yaml
--- a/kustomization.yaml
+++ b/kustomization.yaml
@@ -4,7 +4,8 @@
resources:
- deployment.yaml # main
- service.yaml
+- rg.yaml
images:
- name: app
- newTag: "1.2.3"
+ newTag: "1.3.0"
Derselbe Edit mit yq -i ändert 11 Zeilen: Er entfernt zusätzlich beide
Leerzeilen, rückt die Listen unter resources und images neu ein und
verkürzt die Leerzeichen vor # main.
Tests
Die Unit-Tests rufen das echte yq auf. Sie decken den Leerzeilen-Fall aus
#515 ab, das Anhängen an eine Liste, Kommentare und Abstände in nicht
berührten Zeilen, den Einrückungsstil eingefügter Zeilen, das Löschen eines
Blocks, Dateien mit mehreren Dokumenten, CRLF-Zeilenenden und einen
fehlenden Zeilenumbruch am Dateiende. Ein parametrisierter Test wendet sechs
Ausdrücke, darunter sort_keys(..), eine Umbenennung der Schlüssel mit
with_entries und Edits an Ankern und Merge-Keys, auf eine Datei mit
Block-Skalaren und Flow-Listen an und prüft, dass das Ergebnis dieselben
Daten enthält wie die Ausgabe von yq. Das CI führt die Tests auf Python 3.10 bis 3.13 mit
yq v4.54.1 aus.
Außerdem lief yq-keep über 361 YAML-Dateien (Kubernetes-Manifeste,
CI-Workflows, Compose- und pre-commit-Dateien) mit je fünf Edits. 1.621 von 1.636 Edits wurden
geschrieben. Von den 15 Ablehnungen betrafen 11 Dateien, bei denen schon der
eigene Roundtrip von yq die Daten verändert. Für die 1.088 Edits außer
sort_keys, die eine Datei veränderten, änderte yq-keep insgesamt 2.819
Zeilen und yq -i 26.634.
Grenzen
- Eine geänderte Zeile stammt aus der Ausgabe von yq und hat deren Abstände, zum Beispiel ein Leerzeichen vor dem Kommentar am Zeilenende.
- Edits, die den Großteil einer Datei umordnen oder umschreiben
(
sort_keys(..), Umbenennen aller Schlüssel), ergeben korrekte Daten, aber die Leerzeilen bleiben an ihren alten Positionen. - Wo yq in einem geänderten Bereich einen Kommentar verliert, verliert yq-keep ihn auch.
- Nur YAML wird unterstützt. yq läuft viermal pro Datei, daher ist yq-keep
bei sehr großen Dateien langsamer als
yq -i.
Code und Installationsanleitung liegen unter github.com/Jwrede/yq-keep.