summaryrefslogtreecommitdiffstats
path: root/.github/scripts/add_run_tests_table.py
blob: cb98da6f8c85fb5f21008801f6f01251242f2d47 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env python3
"""
Script to add run tests table comment to PR.
Used by add_run_tests_table.yml workflow.
"""

import os
import json
import urllib.parse
from github import Github, Auth as GithubAuth


def normalize_app_domain(app_domain: str) -> str:
    """Normalize app domain - remove https:// prefix if present."""
    domain = app_domain.strip()
    if domain.startswith("https://"):
        domain = domain[8:]
    if domain.startswith("http://"):
        domain = domain[7:]
    return domain.rstrip('/')


def generate_run_tests_table(pr_number: int, app_domain: str) -> str:
    """Generate run tests button with default parameters (relwithdebinfo, small and medium test sizes)."""
    domain = normalize_app_domain(app_domain)
    base_url = f"https://{domain}/workflow/trigger"
    repo_env = os.environ.get("GITHUB_REPOSITORY")
    if not repo_env or "/" not in repo_env:
        raise ValueError("GITHUB_REPOSITORY environment variable is not set or malformed (expected 'owner/repo')")
    owner, repo = repo_env.split("/", 1)
    workflow_id = "run_tests.yml"
    return_url = f"https://github.com/{owner}/{repo}/pull/{pr_number}"
    
    # Default parameters: relwithdebinfo preset, small and medium test sizes
    params = {
        "owner": owner,
        "repo": repo,
        "workflow_id": workflow_id,
        "ref": "main",
        "pull_number": str(pr_number),
        "test_targets": "ydb/",
        "test_size": "small,medium",
        "additional_ya_make_args": "",
        "build_preset": "relwithdebinfo",  # Default preset
        "collect_coredumps": "false",
        "return_url": return_url
    }
    query_string = "&".join([f"{k}={urllib.parse.quote(str(v), safe='')}" for k, v in params.items()])
    url_ui = f"{base_url}?{query_string}&ui=true"
    
    # Badge with only message (no label) - format: badge/message-color
    # Encode only spaces, keep emoji as is - use two spaces like in backport
    badge_text = "▶  Run tests".replace(" ", "%20")
    button = f"[![▶  Run tests](https://img.shields.io/badge/{badge_text}-4caf50)]({url_ui})"
    
    comment = "<!-- run-tests-table -->\n"
    comment += "<h3>Run Extra Tests</h3>\n\n"
    comment += "Run additional tests for this PR. You can customize:\n"
    comment += "- **Test Size**: small, medium, large (default: small, medium)\n"
    comment += "- **Test Targets**: any directory path (default: `ydb/`)\n"
    comment += "- **Sanitizers**: ASAN, MSAN, TSAN\n"
    comment += "- **Coredumps**: enable for debugging (default: off)\n"
    comment += "- **Additional args**: custom ya make arguments\n\n"
    comment += button
    return comment


def create_or_update_pr_comment(pr, app_domain: str) -> None:
    """Create or update run tests table comment on PR.
    
    Args:
        pr: GitHub PullRequest object
        app_domain: Application domain for workflow URLs
    """
    try:
        pr_number = pr.number
        
        run_tests_table = generate_run_tests_table(pr_number, app_domain)
        header = "<!-- run-tests-table -->"
        
        # Check if comment with run tests table already exists
        existing_comment = None
        for comment in pr.get_issue_comments():
            if comment.body.startswith(header):
                existing_comment = comment
                break
        
        if existing_comment:
            # Update existing comment
            existing_comment.edit(run_tests_table)
            print(f"::notice::Updated run tests table comment on PR #{pr_number}")
        else:
            # Create new comment
            pr.create_issue_comment(run_tests_table)
            print(f"::notice::Created run tests table comment on PR #{pr_number}")
    except Exception as e:
        print(f"::error::Failed to create/update comment on PR #{pr_number}: {e}")
        raise


def main():
    """Main function to add run tests table to PR."""
    # Get PR info - either from event or from workflow_dispatch input
    pr_number_from_input = os.environ.get("PR_NUMBER")
    github_token = os.environ.get("GITHUB_TOKEN")
    github_repo = os.environ.get("GITHUB_REPOSITORY")
    
    if not github_token:
        raise ValueError("GITHUB_TOKEN environment variable is not set")
    if not github_repo:
        raise ValueError("GITHUB_REPOSITORY environment variable is not set")
    
    gh = Github(auth=GithubAuth.Token(github_token))
    repo = gh.get_repo(github_repo)
    
    if pr_number_from_input:
        # workflow_dispatch mode - get PR by number
        pr_number = int(pr_number_from_input)
        pr = repo.get_pull(pr_number)
        print(f"::notice::workflow_dispatch mode: Adding run tests table to PR #{pr_number}")
    else:
        # pull_request event mode - get PR from event
        event_path = os.environ.get("GITHUB_EVENT_PATH")
        if not event_path:
            raise ValueError("GITHUB_EVENT_PATH environment variable is not set")
        
        if not os.path.exists(event_path):
            raise FileNotFoundError(f"Event file not found: {event_path}")
        
        with open(event_path, 'r') as f:
            event = json.load(f)
        
        if "pull_request" not in event:
            raise ValueError("Event does not contain pull_request data")
        
        pr_number = event["pull_request"]["number"]
        pr = repo.get_pull(pr_number)
    
    app_domain = os.environ.get("APP_DOMAIN")
    if not app_domain:
        raise ValueError("APP_DOMAIN environment variable is not set")
    
    create_or_update_pr_comment(pr, app_domain)


if __name__ == "__main__":
    main()