From 5e57a1ca0c91ff9161f829515522067f06abb5ea Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Tue, 24 Feb 2026 12:32:50 +0000 Subject: [PATCH] Updates checks script --- lib/checks/check-additions.py | 30 +++++++++++++++++++++++------- lib/checks/check-readme-edits.py | 18 ------------------ lib/checks/check-yaml-diff.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/lib/checks/check-additions.py b/lib/checks/check-additions.py index 30cad8a..f972f1e 100644 --- a/lib/checks/check-additions.py +++ b/lib/checks/check-additions.py @@ -150,30 +150,46 @@ def check_single_entry(diff): return None -def build_name_index(head): - """Build {lowercase_name: "category > section"} from all services.""" +def _added_keys(diff): + """Build a set of (category, section, lowercase_name) for added services.""" + keys = set() + for svc in diff.get("services", {}).get("added", []): + name = svc.get("fields", {}).get("name", "").lower().strip() + keys.add((svc.get("category", ""), svc.get("section", ""), name)) + return keys + + +def build_name_index(head, diff): + """Build {lowercase_name: "category > section"} from all services, excluding additions.""" index = {} if not head: return index + exclude = _added_keys(diff) for cat in head.get("categories", []): cn = cat.get("name", "") for sec in cat.get("sections", []): sn = sec.get("name", "") for svc in sec.get("services", []): name = svc.get("name", "").lower().strip() - if name: + if name and (cn, sn, name) not in exclude: index[name] = f"{cn} > {sn}" return index -def build_url_index(head): - """Build {url: service_name} from all services, skipping empty URLs.""" +def build_url_index(head, diff): + """Build {url: service_name} from all services, excluding additions.""" index = {} if not head: return index + exclude = _added_keys(diff) for cat in head.get("categories", []): + cn = cat.get("name", "") for sec in cat.get("sections", []): + sn = sec.get("name", "") for svc in sec.get("services", []): + name = svc.get("name", "").lower().strip() + if (cn, sn, name) in exclude: + continue url = svc.get("url", "") if url: index[url] = svc.get("name", "") @@ -245,8 +261,8 @@ def main(): if finding: findings.append(finding) - name_index = build_name_index(head) - url_index = build_url_index(head) + name_index = build_name_index(head, diff) + url_index = build_url_index(head, diff) finding = check_duplicate_name(diff, name_index) if finding: diff --git a/lib/checks/check-readme-edits.py b/lib/checks/check-readme-edits.py index aa733b5..4aa5598 100644 --- a/lib/checks/check-readme-edits.py +++ b/lib/checks/check-readme-edits.py @@ -72,23 +72,6 @@ def get_changed_line_numbers(base_ref): return changed_lines - -def write_step_summary(): - summary_file = os.environ.get("GITHUB_STEP_SUMMARY") - if not summary_file: - return - - lines = [ - "## Direct README Edit Detected\n", - "This PR directly modifies the auto-generated section of `.github/README.md` " - "(between `` and ``).\n", - "**Please edit `awesome-privacy.yml` instead.** The README is regenerated automatically from that file.\n", - ] - - with open(summary_file, "a") as f: - f.write("\n".join(lines) + "\n") - - def main(): parser = argparse.ArgumentParser(description="Check for direct README edits to generated section") parser.add_argument("--base-ref", required=True, help="Base git ref to diff against") @@ -112,7 +95,6 @@ def main(): if start_line <= line_num <= end_line: print(red("Direct edits to the generated section of the README are not allowed."), file=sys.stderr) print(red("Edit awesome-privacy.yml instead and the README will be regenerated."), file=sys.stderr) - write_step_summary() sys.exit(EXIT_FAIL) print(green("README changes are outside the generated section, OK.")) diff --git a/lib/checks/check-yaml-diff.py b/lib/checks/check-yaml-diff.py index a29e096..5ddc798 100644 --- a/lib/checks/check-yaml-diff.py +++ b/lib/checks/check-yaml-diff.py @@ -90,6 +90,23 @@ def write_github_output(name, value): f.write(f"{name}={value}\n") +def find_duplicate_names(data): + """Find duplicate service names within the same section.""" + duplicates = [] + for cat in data.get("categories", []): + cn = cat.get("name", "") + for sec in cat.get("sections", []): + sn = sec.get("name", "") + seen = {} + for svc in sec.get("services", []): + name = svc.get("name", "") + if name in seen: + duplicates.append((cn, sn, name)) + else: + seen[name] = True + return duplicates + + def fmt_path(key): """Format a tuple key as a readable path.""" return " → ".join(key) if isinstance(key, tuple) else key @@ -126,6 +143,11 @@ def write_step_summary(diff_result): bullets.append(f"- Added category **{change['category']}**") else: bullets.append(f"- Removed category **{change['category']}**") + for dup in diff_result.get("duplicates", []): + bullets.append( + f"- ⚠️ Duplicate service name **{dup['service']}** " + f"in {dup['category']} → {dup['section']}" + ) if bullets: lines.extend(bullets) @@ -175,10 +197,15 @@ def main(): for k in cat_removed: categories.append({"category": k, "change_type": "removed_category"}) + duplicates = find_duplicate_names(head) + dup_entries = [{"category": d[0], "section": d[1], "service": d[2]} + for d in duplicates] + diff_result = { "services": {"added": added, "removed": removed, "modified": modified}, "sections": sections, "categories": categories, + "duplicates": dup_entries, } with open(DIFF_OUTPUT_PATH, "w") as f: @@ -196,6 +223,11 @@ def main(): print(red(f"Single-entry rule violation: {len(added_sections)} section additions found."), file=sys.stderr) sys.exit(EXIT_RULE_VIOLATION) + if duplicates: + names = ", ".join(f"{d[2]} (in {d[0]} → {d[1]})" for d in duplicates) + print(red(f"Duplicate service names found: {names}"), file=sys.stderr) + sys.exit(EXIT_RULE_VIOLATION) + total = len(added) + len(removed) + len(modified) print(green(f"Single-entry rule passed. {total} service " f"({added_count} added), {len(sections)} section, "