Skip to content

Commit

Permalink
Merge pull request #54557 from ClickHouse/backport/23.3/54441
Browse files Browse the repository at this point in the history
Backport #54441 to 23.3: Update automated commit status comment
  • Loading branch information
vdimir committed Sep 13, 2023
2 parents 5dc1611 + 2be63a7 commit 100b980
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 40 deletions.
102 changes: 72 additions & 30 deletions tests/ci/commit_status_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import os
import time
from typing import Dict, List, Optional, Union
from collections import defaultdict
import logging

from github import Github
from github.GithubObject import _NotSetType, NotSet as NotSet # type: ignore
from github.Commit import Commit
from github.CommitStatus import CommitStatus
from github.IssueComment import IssueComment
from github.PullRequest import PullRequest
from github.Repository import Repository

from ci_config import CI_CONFIG, REQUIRED_CHECKS, CHECK_DESCRIPTIONS, CheckDescription
Expand Down Expand Up @@ -128,6 +130,27 @@ def post_commit_status(
logging.error("Failed to update the status comment, continue anyway")


STATUS_ICON_MAP = defaultdict(
str,
{
ERROR: "❌",
FAILURE: "❌",
PENDING: "⏳",
SUCCESS: "✅",
},
)


def update_pr_status_label(pr: PullRequest, status: str) -> None:
new_label = "pr-status-" + STATUS_ICON_MAP[status]
for label in pr.get_labels():
if label.name == new_label:
return
if label.name.startswith("pr-status-"):
pr.remove_from_labels(label.name)
pr.add_to_labels(new_label)


def set_status_comment(commit: Commit, pr_info: PRInfo) -> None:
"""It adds or updates the comment status to all Pull Requests but for release
one, so the method does nothing for simple pushes and pull requests with
Expand Down Expand Up @@ -167,6 +190,8 @@ def set_status_comment(commit: Commit, pr_info: PRInfo) -> None:
comment = ic
break

update_pr_status_label(pr, get_worst_state(statuses))

if comment is None:
pr.create_issue_comment(comment_body)
return
Expand All @@ -180,33 +205,16 @@ def set_status_comment(commit: Commit, pr_info: PRInfo) -> None:
def generate_status_comment(pr_info: PRInfo, statuses: CommitStatuses) -> str:
"""The method generates the comment body, as well it updates the CI report"""

def beauty_state(state: str) -> str:
if state == SUCCESS:
return f"🟢 {state}"
if state == PENDING:
return f"🟡 {state}"
if state in [ERROR, FAILURE]:
return f"🔴 {state}"
return state

report_url = create_ci_report(pr_info, statuses)
worst_state = get_worst_state(statuses)
if not worst_state:
# Theoretically possible, although
# the function should not be used on empty statuses
worst_state = "The commit doesn't have the statuses yet"
else:
worst_state = f"The overall status of the commit is {beauty_state(worst_state)}"

comment_body = (
f"<!-- automatic status comment for PR #{pr_info.number} "
f"from {pr_info.head_name}:{pr_info.head_ref} -->\n"
f"This is an automated comment for commit {pr_info.sha} with "
f"description of existing statuses. It's updated for the latest CI running\n"
f"The full report is available [here]({report_url})\n"
f"{worst_state}\n\n<table>"
"<thead><tr><th>Check name</th><th>Description</th><th>Status</th></tr></thead>\n"
"<tbody>"
f"*This is an automated comment for commit {pr_info.sha} with "
f"description of existing statuses. It's updated for the latest CI running*\n\n"
f"[{STATUS_ICON_MAP[worst_state]} Click here]({report_url}) to open a full report in a separate page\n"
f"\n"
)
# group checks by the name to get the worst one per each
grouped_statuses = {} # type: Dict[CheckDescription, CommitStatuses]
Expand All @@ -230,17 +238,46 @@ def beauty_state(state: str) -> str:
else:
grouped_statuses[cd] = [status]

table_rows = [] # type: List[str]
table_header = (
"<table>\n"
"<thead><tr><th>Check name</th><th>Description</th><th>Status</th></tr></thead>\n"
"<tbody>\n"
)
table_footer = "<tbody>\n</table>\n"

details_header = "<details><summary>Successful checks</summary>\n"
details_footer = "</details>\n"

visible_table_rows = [] # type: List[str]
hidden_table_rows = [] # type: List[str]
for desc, gs in grouped_statuses.items():
table_rows.append(
state = get_worst_state(gs)
table_row = (
f"<tr><td>{desc.name}</td><td>{desc.description}</td>"
f"<td>{beauty_state(get_worst_state(gs))}</td></tr>\n"
f"<td>{STATUS_ICON_MAP[state]} {state}</td></tr>\n"
)
if state == SUCCESS:
hidden_table_rows.append(table_row)
else:
visible_table_rows.append(table_row)

table_rows.sort()
result = [comment_body]

comment_footer = "</table>"
return "".join([comment_body, *table_rows, comment_footer])
if hidden_table_rows:
hidden_table_rows.sort()
result.append(details_header)
result.append(table_header)
result.extend(hidden_table_rows)
result.append(table_footer)
result.append(details_footer)

if visible_table_rows:
visible_table_rows.sort()
result.append(table_header)
result.extend(visible_table_rows)
result.append(table_footer)

return "".join(result)


def get_worst_state(statuses: CommitStatuses) -> str:
Expand All @@ -252,10 +289,15 @@ def create_ci_report(pr_info: PRInfo, statuses: CommitStatuses) -> str:
to S3 tests bucket. Then it returns the URL"""
test_results = [] # type: TestResults
for status in statuses:
log_urls = None
log_urls = []
if status.target_url is not None:
log_urls = [status.target_url]
test_results.append(TestResult(status.context, status.state, log_urls=log_urls))
log_urls.append(status.target_url)
raw_logs = status.description or None
test_results.append(
TestResult(
status.context, status.state, log_urls=log_urls, raw_logs=raw_logs
)
)
return upload_results(
S3Helper(), pr_info.number, pr_info.sha, test_results, [], CI_STATUS_NAME
)
Expand Down
20 changes: 10 additions & 10 deletions tests/ci/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ def get_worst_status(statuses: Iterable[str]) -> str:
p.links a:hover {{ background: var(--menu-hover-background); color: var(--menu-hover-color); }}
th {{ cursor: pointer; }}
tr:hover {{ filter: var(--tr-hover-filter); }}
.failed {{ cursor: pointer; }}
.failed-content {{ display: none; }}
.expandable {{ cursor: pointer; }}
.expandable-content {{ display: none; }}
#fish {{ display: none; float: right; position: relative; top: -20em; right: 2vw; margin-bottom: -20em; width: 30vw; filter: brightness(7%); z-index: -1; }}
.themes {{
Expand Down Expand Up @@ -148,7 +148,7 @@ def get_worst_status(statuses: Iterable[str]) -> str:
const getCellValue = (tr, idx) => {{
var classes = tr.classList;
var elem = tr;
if (classes.contains("failed-content") || classes.contains("failed-content.open"))
if (classes.contains("expandable-content") || classes.contains("expandable-content.open"))
elem = tr.previousElementSibling;
return elem.children[idx].innerText || elem.children[idx].textContent;
}}
Expand All @@ -164,9 +164,9 @@ def get_worst_status(statuses: Iterable[str]) -> str:
.forEach(tr => table.appendChild(tr) );
}})));
Array.from(document.getElementsByClassName("failed")).forEach(tr => tr.addEventListener('click', function() {{
Array.from(document.getElementsByClassName("expandable")).forEach(tr => tr.addEventListener('click', function() {{
var content = this.nextElementSibling;
content.classList.toggle("failed-content");
content.classList.toggle("expandable-content");
}}));
let theme = 'dark';
Expand Down Expand Up @@ -546,9 +546,8 @@ def create_test_html_report(
has_log_urls = True

row = []
has_error = test_result.status in ("FAIL", "NOT_FAILED")
if has_error and test_result.raw_logs is not None:
row.append('<tr class="failed">')
if test_result.raw_logs is not None:
row.append('<tr class="expandable">')
else:
row.append("<tr>")
row.append(f"<td>{test_result.name}</td>")
Expand All @@ -557,6 +556,7 @@ def create_test_html_report(

# Allow to quickly scroll to the first failure.
fail_id = ""
has_error = test_result.status in ("FAIL", "NOT_FAILED")
if has_error:
num_fails = num_fails + 1
fail_id = f'id="fail{num_fails}" '
Expand All @@ -578,11 +578,11 @@ def create_test_html_report(
colspan += 1

row.append("</tr>")
rows_part.append("".join(row))
rows_part.append("\n".join(row))
if test_result.raw_logs is not None:
raw_logs = escape(test_result.raw_logs)
row_raw_logs = (
'<tr class="failed-content">'
'<tr class="expandable-content">'
f'<td colspan="{colspan}"><pre>{raw_logs}</pre></td>'
"</tr>"
)
Expand Down

0 comments on commit 100b980

Please sign in to comment.