55Conditional blocks (`- if: ... then: [...]`) stay at the end; their inner
66`then:` lists are also sorted.
77
8+ Also validates that each condition (e.g. "win", "not win", "linux") appears
9+ in at most one `- if:` block per top-level list key. Two separate blocks for
10+ the same condition are never wrong on their own, but they're a standing
11+ foot-gun: a future addition can land in the "wrong" one by accident, and
12+ nothing merges the two, so the same package can end up skipped from one
13+ angle and not the other depending on which block someone edits. Raises
14+ DuplicateConditionError (exit 1) rather than silently merging them, since
15+ merging by hand needs a human to reconcile any per-item comments correctly.
16+
817Usage:
918 vinca-sort-vinca-lists [FILE]
1019 vinca-sort-vinca-lists --check [FILE]
2736RE_SIMPLE_ITEM = re .compile (r"^ - (\S.*)$" )
2837# Regex for the start of a conditional block: " - if: ..."
2938RE_IF_BLOCK = re .compile (r"^ - if:" )
39+ # Regex capturing the condition text of a " - if: <condition>" line
40+ RE_IF_CONDITION = re .compile (r"^ - if:\s*(.+?)\s*$" )
3041# Regex for a then-list item inside a conditional block: " - value"
3142RE_THEN_ITEM = re .compile (r"^ - (\S.*)$" )
3243# Regex for a top-level key
3344RE_TOP_KEY = re .compile (r"^(\S+):" )
3445
3546
47+ class DuplicateConditionError (ValueError ):
48+ """Raised when the same `- if:` condition appears in more than one block
49+ under the same top-level list key."""
50+
51+
3652def _sort_key (line : str ) -> str :
3753 """Extract sortable value from a list item line (lowercase, ignore comments)."""
3854 m = RE_SIMPLE_ITEM .match (line ) or RE_THEN_ITEM .match (line )
@@ -57,6 +73,7 @@ def sort_vinca_lists(path: Path) -> bool:
5773 # Check if this line starts a target list key
5874 m = RE_TOP_KEY .match (line )
5975 if m and m .group (1 ) in LISTS_TO_SORT :
76+ list_key = m .group (1 )
6077 result .append (line )
6178 i += 1
6279
@@ -136,6 +153,25 @@ def sort_vinca_lists(path: Path) -> bool:
136153 if current_if_block is not None :
137154 if_blocks .append (current_if_block )
138155
156+ # Reject duplicate conditions: two separate "- if:" blocks for the
157+ # same condition under the same list key. See module docstring.
158+ seen_conditions = {}
159+ for block in if_blocks :
160+ cond_match = RE_IF_CONDITION .match (block [0 ])
161+ if not cond_match :
162+ continue
163+ condition = cond_match .group (1 )
164+ if condition in seen_conditions :
165+ raise DuplicateConditionError (
166+ f"{ list_key } : condition { condition !r} appears in more "
167+ f"than one '- if:' block. Merge them into a single "
168+ f"block (each item may need its own comment moved "
169+ f"inline first, since sorting the merged then: list "
170+ f"can otherwise separate a standalone comment from "
171+ f"the item it was meant to describe)."
172+ )
173+ seen_conditions [condition ] = True
174+
139175 # Sort simple items
140176 sorted_simple = sorted (simple_items , key = _sort_key )
141177 if sorted_simple != simple_items :
@@ -221,14 +257,20 @@ def main():
221257 print (f"ERROR: { args .file } not found" , file = sys .stderr )
222258 sys .exit (1 )
223259
224- if args .check :
225- with tempfile .NamedTemporaryFile (suffix = ".yaml" , delete = False ) as tf :
226- tmp = Path (tf .name )
227- shutil .copy2 (args .file , tmp )
228- changed = sort_vinca_lists (tmp )
229- tmp .unlink (missing_ok = True )
230- else :
231- changed = sort_vinca_lists (args .file )
260+ try :
261+ if args .check :
262+ with tempfile .NamedTemporaryFile (suffix = ".yaml" , delete = False ) as tf :
263+ tmp = Path (tf .name )
264+ shutil .copy2 (args .file , tmp )
265+ try :
266+ changed = sort_vinca_lists (tmp )
267+ finally :
268+ tmp .unlink (missing_ok = True )
269+ else :
270+ changed = sort_vinca_lists (args .file )
271+ except DuplicateConditionError as e :
272+ print (f"ERROR: { args .file } : { e } " , file = sys .stderr )
273+ sys .exit (1 )
232274
233275 status = ("UNSORTED" if args .check else "SORTED" ) if changed else "OK"
234276 print (f"{ status } : { args .file } " )
0 commit comments