Skip to content

Commit 4830f8c

Browse files
Add draft PR processing and manual exclusion support (#3636)
Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent 8e19db8 commit 4830f8c

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
lines changed

.github/workflows/update-test-rules.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ jobs:
6868
ADD_TEST_RULES_LABEL: 'true'
6969
IN_TEST_RULES_LABEL: 'in-test-rules'
7070
AUTHOR_MEMBERSHIP_EXCLUSION_LABEL: 'test-rules:excluded:author_membership'
71+
MANUAL_EXCLUSION_LABEL: 'test-rules:excluded:manual'
7172

7273
run: python scripts/sync_detection_rules.py
7374

scripts/sync_detection_rules.py

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@
6868
IN_TEST_RULES_LABEL = os.getenv('IN_TEST_RULES_LABEL', 'in-test-rules')
6969
# label to apply to PRs that are excluded due to author membership
7070
AUTHOR_MEMBERSHIP_EXCLUSION_LABEL = os.getenv('AUTHOR_MEMBERSHIP_EXCLUSION_LABEL', 'test-rules:excluded:author_membership')
71+
# label to apply to PRs that are manually excluded (by removing in-test-rules label)
72+
MANUAL_EXCLUSION_LABEL = os.getenv('MANUAL_EXCLUSION_LABEL', 'test-rules:excluded:manual')
7173

7274
# flag to skip files containing specific text patterns
7375
# this is due to test-rules not supporting specific functions
@@ -570,6 +572,23 @@ def save_file(path, content):
570572
file.write(content)
571573

572574

575+
def pr_has_synced_files(pr_number):
576+
"""
577+
Check if a PR has any synced files in the output folder.
578+
579+
Args:
580+
pr_number (int): Pull request number
581+
582+
Returns:
583+
bool: True if files exist for this PR, False otherwise
584+
"""
585+
prefix = f"{pr_number}_"
586+
for filename in os.listdir(OUTPUT_FOLDER):
587+
if filename.startswith(prefix) and filename.endswith('.yml'):
588+
return True
589+
return False
590+
591+
573592
def clean_output_folder(valid_files):
574593
for filename in os.listdir(OUTPUT_FOLDER):
575594
file_path = os.path.join(OUTPUT_FOLDER, filename)
@@ -829,15 +848,35 @@ def handle_pr_rules(mode):
829848

830849
for pr in pull_requests:
831850
# Common checks for all modes
851+
# Draft PRs are skipped unless user explicitly added the in-test-rules label
832852
if pr['draft']:
833-
print(f"Skipping draft PR #{pr['number']}: {pr['title']}")
834-
continue
853+
if ADD_TEST_RULES_LABEL and has_label(pr['number'], IN_TEST_RULES_LABEL):
854+
print(f"Processing draft PR #{pr['number']} (has '{IN_TEST_RULES_LABEL}' label): {pr['title']}")
855+
else:
856+
print(f"Skipping draft PR #{pr['number']}: {pr['title']}")
857+
continue
835858
if pr['base']['ref'] != 'main':
836859
print(f"Skipping non-main branch PR #{pr['number']}: {pr['title']} -- dest branch: {pr['base']['ref']}")
837860
continue
838861

839862
pr_number = pr['number']
840863

864+
# Check for manual exclusion label (user opted out of test-rules)
865+
if ADD_TEST_RULES_LABEL and has_label(pr_number, MANUAL_EXCLUSION_LABEL):
866+
print(f"Skipping manually excluded PR #{pr_number}: {pr['title']}")
867+
# Remove in-test-rules label if both are present (manual exclusion takes precedence)
868+
if has_label(pr_number, IN_TEST_RULES_LABEL):
869+
print(f"\tRemoving '{IN_TEST_RULES_LABEL}' label since manual exclusion takes precedence")
870+
remove_label(pr_number, IN_TEST_RULES_LABEL)
871+
continue
872+
873+
# Check if user removed the in-test-rules label (opt-out)
874+
# If PR has synced files but no in-test-rules label, user must have removed it
875+
if ADD_TEST_RULES_LABEL and pr_has_synced_files(pr_number) and not has_label(pr_number, IN_TEST_RULES_LABEL):
876+
print(f"PR #{pr_number} has synced files but '{IN_TEST_RULES_LABEL}' label was removed - applying manual exclusion")
877+
apply_label(pr_number, MANUAL_EXCLUSION_LABEL)
878+
continue
879+
841880
# Organization membership and comment trigger checks (for any mode if flags are set)
842881
process_pr = True
843882
print(f"Processing PR #{pr_number}: {pr['title']}")
@@ -867,7 +906,11 @@ def handle_pr_rules(mode):
867906
if not has_label(pr_number, AUTHOR_MEMBERSHIP_EXCLUSION_LABEL):
868907
print(f"\tPR #{pr_number} doesn't have the '{AUTHOR_MEMBERSHIP_EXCLUSION_LABEL}' label. Applying...")
869908
apply_label(pr_number, AUTHOR_MEMBERSHIP_EXCLUSION_LABEL)
870-
909+
910+
# Remove in-test-rules label if previously applied
911+
if ADD_TEST_RULES_LABEL and has_label(pr_number, IN_TEST_RULES_LABEL):
912+
remove_label(pr_number, IN_TEST_RULES_LABEL)
913+
871914
process_pr = False
872915

873916
if not process_pr:
@@ -882,6 +925,9 @@ def handle_pr_rules(mode):
882925
if not has_required_action_completed(latest_sha, REQUIRED_CHECK_NAME, REQUIRED_CHECK_CONCLUSION):
883926
print(
884927
f"\tSkipping PR #{pr_number}: Required check '{REQUIRED_CHECK_NAME}' has not completed with conclusion '{REQUIRED_CHECK_CONCLUSION}'")
928+
# Remove in-test-rules label if previously applied
929+
if ADD_TEST_RULES_LABEL and has_label(pr_number, IN_TEST_RULES_LABEL):
930+
remove_label(pr_number, IN_TEST_RULES_LABEL)
885931
continue
886932

887933
files = get_files_for_pull_request(pr_number)
@@ -891,12 +937,16 @@ def handle_pr_rules(mode):
891937
yaml_rule_count = count_yaml_rules_in_pr(files)
892938
if yaml_rule_count > MAX_RULES_PER_PR:
893939
print(f"\tSkipping PR #{pr_number}: Contains {yaml_rule_count} YAML rules (max allowed: {MAX_RULES_PER_PR})")
894-
940+
895941
# Apply label to indicate PR was skipped due to too many rules
896942
if not has_label(pr_number, BULK_PR_LABEL):
897943
print(f"\tPR #{pr_number} doesn't have the '{BULK_PR_LABEL}' label. Applying...")
898944
apply_label(pr_number, BULK_PR_LABEL)
899-
945+
946+
# Remove in-test-rules label if previously applied
947+
if ADD_TEST_RULES_LABEL and has_label(pr_number, IN_TEST_RULES_LABEL):
948+
remove_label(pr_number, IN_TEST_RULES_LABEL)
949+
900950
continue
901951
else:
902952
# if it has the label, remove it.

0 commit comments

Comments
 (0)