Skip to content

Commit 83b6779

Browse files
committed
validate_yaml: detect duplicate job definitions
Signed-off-by: Denys Fedoryshchenko <denys.f@collabora.com>
1 parent c66631d commit 83b6779

File tree

1 file changed

+52
-0
lines changed

1 file changed

+52
-0
lines changed

tests/validate_yaml.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import yaml
99
import sys
1010
import argparse
11+
import re
1112

1213
VERBOSE = False
1314

@@ -171,6 +172,8 @@ def merge_files(dir="config"):
171172
with open(file, "r") as stream:
172173
try:
173174
data = yaml.safe_load(stream)
175+
# Check for duplicate jobs in this file before merging
176+
validate_duplicate_jobs(data, file)
174177
merged_data = recursive_merge(merged_data, data)
175178
except yaml.YAMLError as exc:
176179
print(f"Error in {file}: {exc}")
@@ -338,6 +341,55 @@ def walker(merged_data):
338341
return
339342

340343

344+
def validate_duplicate_jobs(data, filename):
345+
"""
346+
Check for duplicate job keys in a single YAML file
347+
This catches cases where the same job name appears multiple times
348+
"""
349+
if 'jobs' not in data:
350+
return
351+
352+
# Parse the file as raw text to detect duplicate keys
353+
with open(filename, 'r') as f:
354+
content = f.read()
355+
356+
# Look for job definitions in the jobs section
357+
# Find the jobs section
358+
jobs_match = re.search(r'^jobs:\s*$', content, re.MULTILINE)
359+
if not jobs_match:
360+
return
361+
362+
# Extract everything after the jobs: line until next top-level key or end of file
363+
jobs_start = jobs_match.end()
364+
jobs_section = content[jobs_start:]
365+
366+
# Find the end of jobs section (next top-level key or end of file)
367+
next_section_match = re.search(r'^\S:', jobs_section, re.MULTILINE)
368+
if next_section_match:
369+
jobs_section = jobs_section[:next_section_match.start()]
370+
371+
# Find all job names (lines that start with 2 spaces and contain a colon)
372+
job_pattern = r'^\s{2}([a-zA-Z0-9_\-]+):'
373+
job_matches = re.findall(job_pattern, jobs_section, re.MULTILINE)
374+
if VERBOSE:
375+
print(f"Found {len(job_matches)} job definitions in {filename}")
376+
377+
# Count occurrences of each job name
378+
job_counts = {}
379+
for job_name in job_matches:
380+
job_counts[job_name] = job_counts.get(job_name, 0) + 1
381+
382+
# Report duplicates
383+
duplicates_found = False
384+
for job_name, count in job_counts.items():
385+
if count > 1:
386+
print(f"ERROR: Duplicate job '{job_name}' found {count} times in {filename}")
387+
duplicates_found = True
388+
389+
if duplicates_found:
390+
raise yaml.YAMLError(f"Duplicate job definitions found in {filename}")
391+
392+
341393
def main():
342394
global VERBOSE
343395
parser = argparse.ArgumentParser(description="Validate and dump yaml files")

0 commit comments

Comments
 (0)