Skip to content

Commit

Permalink
testutils.github: make reporting comment sticky
Browse files Browse the repository at this point in the history
UNTESTED!

Also remember RIOT-OS#215!
  • Loading branch information
miri64 committed Apr 23, 2021
1 parent 676d417 commit d4b6772
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 19 deletions.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
aiocoap[linkheader]>=0.4b3
beautifulsoup4
iotlabcli
pygithub
pytest
Expand Down
140 changes: 121 additions & 19 deletions testutils/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
import re
import subprocess

from bs4 import BeautifulSoup
from github import Github, GithubException


STICKY_COMMENT_COMMENT = "<!-- release-specs results {user} -->"
GITHUB_DOMAIN = "github.com"
API_URL = "https://api.%s" % GITHUB_DOMAIN
REPO_NAME = "RIOT-OS/Release-Specs"
GITHUBTOKEN_FILE = ".riotgithubtoken"
OUTCOME_EMOJIS = {
"passed": "&#x2714;",
"failed": "&#x2716;",
"skipped": "&#x1F7E1;",
}


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -124,42 +131,137 @@ def find_task_text(issue_body, tested_task):
return None, None


def make_comment(pytest_report, issue, task):
comment = "### [{:02d}. {}]({})\n\n".format(
task["spec"]["spec"],
task["name"],
task["url"]
)
def find_previous_comment(github, issue):
comment_comment = STICKY_COMMENT_COMMENT.format(user=github.user.name)
for comment in issue.get_comments():
if comment_comment in comment.body:
return comment
return None


def create_comment(github, issue):
body = "<h1>Test Report</h1>\n\n"
body += STICKY_COMMENT_COMMENT.format(user=github.user.name)
body += """
<table>
<thead>
<tr><th></th><th>Task</th><th>Outcome</th></tr>
</thead>
<tbody>
</tbody>
</table>
"""
try:
return issue.create_comment(body)
except GithubException as e:
logger.error("Unable to comment: {}".format(e))
return None


def _generate_outcome_summary(pytest_report):
run_url = None
if "GITHUB_RUN_ID" in os.environ and \
"GITHUB_REPOSITORY" in os.environ and \
"GITHUB_SERVER_URL" in os.environ:
run_url = "{GITHUB_SERVER_URL}/{GITHUB_REPOSITORY}/actions/runs/" \
"{GITHUB_RUN_ID}".format(**os.environ)
comment += "<details><summary><strong>{}{}{}</strong></summary>\n\n" \
outcome = "<details><summary><strong>{}{}{}</strong></summary>\n\n" \
.format(
'<a href="{}">'.format(run_url) if run_url else '',
pytest_report.outcome.upper(), '</a>' if run_url else ''
)
if pytest_report.longrepr:
comment += "###### Failures\n\n"
comment += "```\n"
comment += str(pytest_report.longrepr)
comment += "\n```\n\n"
outcome += "###### Failures\n\n"
outcome += "```\n"
outcome += str(pytest_report.longrepr)
outcome += "\n```\n\n"
if pytest_report.sections:
for title, body in pytest_report.sections:
comment += "###### {}\n\n".format(title)
comment += "```\n"
comment += str(body)
comment += "\n```\n\n"
comment += "</details>"
outcome += "###### {}\n\n".format(title)
outcome += "```\n"
outcome += str(body)
outcome += "\n```\n\n"
outcome += "</details>"
return outcome


def _get_tasks(comment, tbody, task):
tasks = []
task_already_exists = False
for row in tbody.children:
if row.name != 'tr':
continue
cells = row.contents[0]
try:
emoji = cells[0].decode_contents()
task_cell = cells[1].contents[0]
outcome = cells[2]
except IndexError:
logger.error("Unexpected table format in %s:\n%s",
comment, row)
task_title = task.decode_contents().strip()
try:
task_url = task['href']
except KeyError:
logger.error("Unexpected table format in %s:\n%s",
comment, task_cell)
if task_title == task["title"]:
task_already_exists = True
emoji = task["emoji"]
task_url = task["task_url"]
outcome = task["outcome"]
tasks.append((task_title, task_url, emoji, outcome))
if not task_already_exists:
tasks.append((task["title"].strip(), task["url"], task["emoji"],
task["outcome"]))
tasks.sort()


def _update_soup(soup, tbody, tasks):
tbody.clear()
for task in tasks:
tr = soup.new_tag("tr")
tbody.append(tr)
emoji_td = soup.new_tag("td")
emoji_td.string = task[2]
tr.append(emoji_td)
task_td = soup.new_tag("td")
task_a = soup.new_tag("a", href=task[1])
task_a.string = task[0]
task_td.append(task_a)
tr.append(task_td)
outcome_td = soup.new_tag("td")
outcome_td.append(tasks[3])
tr.append(outcome_td)


def update_comment(pytest_report, comment, task):
soup = BeautifulSoup(comment.body, "html.parser")
task["title"] = "{:02d}. {}".format(task["spec"]["spec"], task["name"]) \
.strip()
task["outcome"] = BeautifulSoup(_generate_outcome_summary(pytest_report),
"html.parser").details
task["emoji"] = OUTCOME_EMOJIS[pytest_report.outcome.lower()]
tbody = soup.find('tbody')
if tbody is None:
logger.error("Unable to find table body in %s:\n%s",
comment, comment.body)
return None
_update_soup(soup, tbody, _get_tasks(comment, tbody, task))
try:
return issue.create_comment(comment)
comment.edit(soup.decode_contents())
except GithubException as e:
logger.error("Unable to comment: {}".format(e))
logger.error("Unable to update comment: {}".format(e))
return None


def make_comment(pytest_report, github, issue, task):
comment = find_previous_comment(github, issue)
if comment is None:
comment = create_comment(github, issue)
update_comment(pytest_report, comment, task)


# pylint: disable=R0911,R0912
def update_issue(pytest_report): # noqa: C901
if pytest_report.when != 'call' or pytest_report.outcome == 'skipped':
Expand Down Expand Up @@ -189,7 +291,7 @@ def update_issue(pytest_report): # noqa: C901
logger.warning("Unable to find task {spec}.{task} in the "
"tracking issue".format(**tested_task))
elif not task["done"]:
comment = make_comment(pytest_report, issue, task)
comment = make_comment(pytest_report, github, issue, task)
if comment:
comment_url = comment.html_url
else:
Expand Down

0 comments on commit d4b6772

Please sign in to comment.