2017-11-16 00:43:10 +01:00
|
|
|
from django.http import HttpRequest, HttpResponse
|
|
|
|
|
2020-08-20 00:32:15 +02:00
|
|
|
from zerver.decorator import webhook_view
|
2017-10-31 04:25:48 +01:00
|
|
|
from zerver.lib.request import REQ, has_request_variables
|
2019-02-02 23:53:55 +01:00
|
|
|
from zerver.lib.response import json_success
|
2021-12-17 07:03:22 +01:00
|
|
|
from zerver.lib.validator import WildValue, check_int, check_none_or, check_string, to_wild_value
|
2018-03-13 23:43:02 +01:00
|
|
|
from zerver.lib.webhooks.common import check_send_webhook_message
|
2017-05-02 01:00:50 +02:00
|
|
|
from zerver.models import UserProfile
|
2016-05-09 19:59:33 +02:00
|
|
|
|
2020-06-12 20:19:07 +02:00
|
|
|
outcome_to_formatted_status_map = {
|
|
|
|
"success": "has succeeded",
|
|
|
|
"failed": "has failed",
|
|
|
|
"canceled": "was canceled",
|
|
|
|
}
|
2016-05-09 19:59:33 +02:00
|
|
|
|
2021-07-16 11:40:46 +02:00
|
|
|
ALL_EVENT_TYPES = list(outcome_to_formatted_status_map.keys())
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-07-16 11:40:46 +02:00
|
|
|
|
|
|
|
@webhook_view("CircleCI", all_event_types=ALL_EVENT_TYPES)
|
2016-05-09 19:59:33 +02:00
|
|
|
@has_request_variables
|
2021-02-12 08:19:30 +01:00
|
|
|
def api_circleci_webhook(
|
|
|
|
request: HttpRequest,
|
|
|
|
user_profile: UserProfile,
|
2021-12-17 07:03:22 +01:00
|
|
|
payload: WildValue = REQ(argument_type="body", converter=to_wild_value),
|
2021-02-12 08:19:30 +01:00
|
|
|
) -> HttpResponse:
|
2021-02-12 08:20:45 +01:00
|
|
|
payload = payload["payload"]
|
2016-05-09 19:59:33 +02:00
|
|
|
subject = get_subject(payload)
|
|
|
|
body = get_body(payload)
|
|
|
|
|
2021-07-16 11:40:46 +02:00
|
|
|
check_send_webhook_message(
|
|
|
|
request,
|
|
|
|
user_profile,
|
|
|
|
subject,
|
|
|
|
body,
|
2021-12-17 07:03:22 +01:00
|
|
|
payload["status"].tame(check_string)
|
|
|
|
if "build_num" not in payload
|
|
|
|
else payload["outcome"].tame(check_string),
|
2021-07-16 11:40:46 +02:00
|
|
|
)
|
2022-01-31 13:44:02 +01:00
|
|
|
return json_success(request)
|
2016-05-09 19:59:33 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-12-17 07:03:22 +01:00
|
|
|
def get_subject(payload: WildValue) -> str:
|
|
|
|
return payload["reponame"].tame(check_string)
|
2020-06-12 20:19:07 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-12-17 07:03:22 +01:00
|
|
|
def get_commit_range_info(payload: WildValue) -> str:
|
2020-06-12 20:19:07 +02:00
|
|
|
commits = payload["all_commit_details"]
|
|
|
|
num_commits = len(commits)
|
|
|
|
|
|
|
|
if num_commits == 1:
|
2021-12-17 07:03:22 +01:00
|
|
|
commit_id = commits[0]["commit"].tame(check_string)[:10]
|
|
|
|
commit_url = commits[0]["commit_url"].tame(check_string)
|
2020-06-12 20:19:07 +02:00
|
|
|
return f"- **Commit:** [{commit_id}]({commit_url})"
|
|
|
|
|
2021-12-17 07:03:22 +01:00
|
|
|
vcs_provider = payload["user"]["vcs_type"].tame(check_string) # Same as payload["why"]?
|
|
|
|
first_commit_id = commits[0]["commit"].tame(check_string)
|
2020-06-12 20:19:07 +02:00
|
|
|
shortened_first_commit_id = first_commit_id[:10]
|
2021-12-17 07:03:22 +01:00
|
|
|
last_commit_id = commits[-1]["commit"].tame(check_string)
|
2020-06-12 20:19:07 +02:00
|
|
|
shortened_last_commit_id = last_commit_id[:10]
|
|
|
|
if vcs_provider == "github":
|
|
|
|
# Then use GitHub's commit range feature to form the appropriate url.
|
2021-12-17 07:03:22 +01:00
|
|
|
vcs_url = payload["vcs_url"].tame(check_string)
|
2020-06-12 20:19:07 +02:00
|
|
|
commit_range_url = f"{vcs_url}/compare/{first_commit_id}...{last_commit_id}"
|
|
|
|
return f"- **Commits ({num_commits}):** [{shortened_first_commit_id} ... {shortened_last_commit_id}]({commit_range_url})"
|
|
|
|
else:
|
2020-10-23 02:43:28 +02:00
|
|
|
# Bitbucket doesn't have a good commit range URL feature like GitHub does.
|
2020-06-12 20:19:07 +02:00
|
|
|
# So let's just show the two separately.
|
|
|
|
# https://community.atlassian.com/t5/Bitbucket-questions/BitBucket-4-14-diff-between-any-two-commits/qaq-p/632974
|
2021-12-17 07:03:22 +01:00
|
|
|
first_commit_url = commits[0]["commit_url"].tame(check_string)
|
|
|
|
last_commit_url = commits[-1]["commit_url"].tame(check_string)
|
2020-06-12 20:19:07 +02:00
|
|
|
return f"- **Commits ({num_commits}):** [{shortened_first_commit_id}]({first_commit_url}) ... [{shortened_last_commit_id}]({last_commit_url})"
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-12-17 07:03:22 +01:00
|
|
|
def get_authors_and_committer_info(payload: WildValue) -> str:
|
2020-06-12 20:19:07 +02:00
|
|
|
body = ""
|
|
|
|
|
|
|
|
author_names = set()
|
|
|
|
committer_names = set()
|
|
|
|
for commit in payload["all_commit_details"]:
|
2021-12-17 07:03:22 +01:00
|
|
|
author_name = commit["author_name"].tame(check_string)
|
|
|
|
author_username = commit["author_login"].tame(check_none_or(check_string))
|
|
|
|
if author_username is not None:
|
2020-06-12 20:19:07 +02:00
|
|
|
author_names.add(f"{author_name} ({author_username})")
|
|
|
|
else:
|
2021-12-17 07:03:22 +01:00
|
|
|
author_names.add(author_name)
|
2020-06-12 20:19:07 +02:00
|
|
|
|
2021-12-17 07:03:22 +01:00
|
|
|
if commit["committer_email"].tame(check_none_or(check_string)) is not None:
|
|
|
|
committer_name = commit["committer_name"].tame(check_string)
|
|
|
|
committer_username = commit["committer_login"].tame(check_none_or(check_string))
|
|
|
|
if committer_username is not None:
|
2020-06-12 20:19:07 +02:00
|
|
|
committer_names.add(f"{committer_name} ({committer_username})")
|
|
|
|
else:
|
2021-12-17 07:03:22 +01:00
|
|
|
committer_names.add(committer_name)
|
2020-06-12 20:19:07 +02:00
|
|
|
|
|
|
|
author_names_list = list(author_names)
|
|
|
|
author_names_list.sort()
|
|
|
|
committer_names_list = list(committer_names)
|
|
|
|
committer_names_list.sort()
|
|
|
|
authors = ", ".join(author_names_list)
|
|
|
|
committers = ", ".join(committer_names_list)
|
|
|
|
|
|
|
|
# Add the authors' information to the body.
|
|
|
|
if len(author_names_list) > 1:
|
|
|
|
body += f"- **Authors:** {authors}"
|
|
|
|
else:
|
|
|
|
body += f"- **Author:** {authors}"
|
|
|
|
|
|
|
|
# Add information about the committers if it was provided.
|
|
|
|
if len(committer_names) > 0:
|
|
|
|
if len(committer_names) > 1:
|
|
|
|
body += f"\n- **Committers:** {committers}"
|
|
|
|
else:
|
|
|
|
body += f"\n- **Committer:** {committers}"
|
|
|
|
|
|
|
|
return body
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-12-17 07:03:22 +01:00
|
|
|
def super_minimal_body(payload: WildValue) -> str:
|
|
|
|
branch_name = payload["branch"].tame(check_string)
|
|
|
|
status = payload["status"].tame(check_string)
|
2020-06-15 06:26:53 +02:00
|
|
|
formatted_status = outcome_to_formatted_status_map.get(status, status)
|
2021-12-17 07:03:22 +01:00
|
|
|
build_url = payload["build_url"].tame(check_string)
|
|
|
|
username = payload["username"].tame(check_string)
|
2020-06-15 06:26:53 +02:00
|
|
|
return f"[Build]({build_url}) triggered by {username} on branch `{branch_name}` {formatted_status}."
|
2016-05-09 19:59:33 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-12-17 07:03:22 +01:00
|
|
|
def get_body(payload: WildValue) -> str:
|
|
|
|
if "build_num" not in payload:
|
2020-06-15 06:26:53 +02:00
|
|
|
return super_minimal_body(payload)
|
|
|
|
|
2021-12-17 07:03:22 +01:00
|
|
|
build_num = payload["build_num"].tame(check_int)
|
|
|
|
build_url = payload["build_url"].tame(check_string)
|
2020-06-12 20:19:07 +02:00
|
|
|
|
2021-12-17 07:03:22 +01:00
|
|
|
outcome = payload["outcome"].tame(check_string)
|
2020-06-12 20:19:07 +02:00
|
|
|
formatted_status = outcome_to_formatted_status_map.get(outcome, outcome)
|
|
|
|
|
2021-12-17 07:03:22 +01:00
|
|
|
branch_name = payload["branch"].tame(check_string)
|
|
|
|
workflow_name = payload["workflows"]["workflow_name"].tame(check_string)
|
|
|
|
job_name = payload["workflows"]["job_name"].tame(check_string)
|
2020-06-12 20:19:07 +02:00
|
|
|
|
|
|
|
commit_range_info = get_commit_range_info(payload)
|
|
|
|
pull_request_info = ""
|
|
|
|
if len(payload["pull_requests"]) > 0:
|
2021-12-17 07:03:22 +01:00
|
|
|
pull_request_url = payload["pull_requests"][0]["url"].tame(check_string)
|
2021-05-10 07:02:14 +02:00
|
|
|
pull_request_info = f"- **Pull request:** {pull_request_url}"
|
2020-06-12 20:19:07 +02:00
|
|
|
authors_and_committers_info = get_authors_and_committer_info(payload)
|
|
|
|
|
|
|
|
body = f"""
|
|
|
|
Build [#{build_num}]({build_url}) of `{job_name}`/`{workflow_name}` on branch `{branch_name}` {formatted_status}.
|
|
|
|
{commit_range_info}
|
|
|
|
{pull_request_info}
|
|
|
|
{authors_and_committers_info}
|
|
|
|
""".strip()
|
|
|
|
|
|
|
|
return body
|