mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	Previously, the GoCD integration relied on GoCD's integration with bug tracking and monitoring tools through the users' Config XML file [1]. However, this feature no longer works as expected, as it cannot send HTTPS payloads to an external endpoint. This commit updates our GoCD integration to use Sentry's WebHook notifier GoCD plugin [2] to send webhook payloads from users GoCD server to Zulip. We are using an older version of the plugin—v0.0.6—because the newer version—v0.0.9—doesn't work at this time. Additionally, this change reformats the notifications to include more details on the GoCD event, such as lists of passed and failed jobs. Fixes #21224. Co-authored-by: Pieter CK <pieterceka123@gmail.com> [1]: https://docs.gocd.org/current/integration/#integration-with-bug-tracking-and-story-management-tools [2]: https://github.com/getsentry/gocd-webhook-notification-plugin/releases/tag/v0.0.6
		
			
				
	
	
		
			112 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# Webhooks for external integrations.
 | 
						|
 | 
						|
from collections import defaultdict
 | 
						|
 | 
						|
from django.http import HttpRequest, HttpResponse
 | 
						|
 | 
						|
from zerver.decorator import webhook_view
 | 
						|
from zerver.lib.response import json_success
 | 
						|
from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint
 | 
						|
from zerver.lib.validator import WildValue, check_string
 | 
						|
from zerver.lib.webhooks.common import check_send_webhook_message
 | 
						|
from zerver.lib.webhooks.git import get_short_sha
 | 
						|
from zerver.models import UserProfile
 | 
						|
 | 
						|
COMMIT_INFO_TEMPLATE = """[`{commit_details}`]({commit_link}) on branch `{branch_name}`"""
 | 
						|
TOPIC_TEMPLATE = "{pipeline} / {stage}"
 | 
						|
 | 
						|
SCHEDULED_BODY_TEMPLATE = """
 | 
						|
**Pipeline {status}**: {pipeline} / {stage}
 | 
						|
- **Commit**: {commit_details}
 | 
						|
- **Started**: {start_time}
 | 
						|
"""
 | 
						|
 | 
						|
COMPLETED_BODY_TEMPLATE = """
 | 
						|
{emoji} **Build {status}**: {pipeline} / {stage}
 | 
						|
- **Commit**: {commit_details}
 | 
						|
- **Started**: {start_time}
 | 
						|
- **Finished**: {end_time}
 | 
						|
"""
 | 
						|
 | 
						|
 | 
						|
@webhook_view("Gocd")
 | 
						|
@typed_endpoint
 | 
						|
def api_gocd_webhook(
 | 
						|
    request: HttpRequest,
 | 
						|
    user_profile: UserProfile,
 | 
						|
    *,
 | 
						|
    payload: JsonBodyPayload[WildValue],
 | 
						|
) -> HttpResponse:
 | 
						|
    type = payload["type"].tame(check_string)
 | 
						|
    if type == "stage":
 | 
						|
        body = get_body(payload)
 | 
						|
        topic_name = get_topic(payload)
 | 
						|
        check_send_webhook_message(request, user_profile, topic_name, body)
 | 
						|
    return json_success(request)
 | 
						|
 | 
						|
 | 
						|
def get_topic(payload: WildValue) -> str:
 | 
						|
    return TOPIC_TEMPLATE.format(
 | 
						|
        pipeline=payload["data"]["pipeline"]["name"].tame(check_string),
 | 
						|
        stage=payload["data"]["pipeline"]["stage"]["name"].tame(check_string),
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def get_commit_details(payload: WildValue) -> str:
 | 
						|
    build = payload["data"]["pipeline"]["build-cause"][0]
 | 
						|
    material = build["material"]
 | 
						|
    url_base = material["git-configuration"]["url"].tame(check_string)
 | 
						|
    revision = build["modifications"][0]["revision"].tame(check_string)
 | 
						|
    commit_sha = get_short_sha(revision)
 | 
						|
    url = f"{url_base}/commit/{commit_sha}"
 | 
						|
    branch = material["git-configuration"]["branch"].tame(check_string)
 | 
						|
    return COMMIT_INFO_TEMPLATE.format(
 | 
						|
        commit_details=commit_sha,
 | 
						|
        commit_link=url,
 | 
						|
        branch_name=branch,
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def get_jobs_details(pipeline_data: WildValue) -> str:
 | 
						|
    job_dict_list = pipeline_data["stage"]["jobs"]
 | 
						|
    formatted_job_dict = defaultdict(list)
 | 
						|
    job_details_template = ""
 | 
						|
 | 
						|
    for job in job_dict_list:
 | 
						|
        job_name = job["name"].tame(check_string)
 | 
						|
        job_result = job["result"].tame(check_string)
 | 
						|
        formatted_job_dict[job_result].append(f"`{job_name}`")
 | 
						|
 | 
						|
    for key in formatted_job_dict:
 | 
						|
        formatted_job_list = ", ".join(formatted_job_dict[key])
 | 
						|
        job_details_template += f"- **{key}**: {formatted_job_list}\n"
 | 
						|
 | 
						|
    return job_details_template
 | 
						|
 | 
						|
 | 
						|
def get_body(payload: WildValue) -> str:
 | 
						|
    pipeline_data = payload["data"]["pipeline"]
 | 
						|
    body_details = {
 | 
						|
        "commit_details": get_commit_details(payload),
 | 
						|
        "status": pipeline_data["stage"]["state"].tame(check_string).lower(),
 | 
						|
        "pipeline": pipeline_data["name"].tame(check_string),
 | 
						|
        "stage": pipeline_data["stage"]["name"].tame(check_string),
 | 
						|
        "start_time": pipeline_data["stage"]["create-time"].tame(check_string),
 | 
						|
    }
 | 
						|
 | 
						|
    if body_details["status"] == "building":
 | 
						|
        return SCHEDULED_BODY_TEMPLATE.format(**body_details)
 | 
						|
 | 
						|
    result = pipeline_data["stage"]["result"].tame(check_string)
 | 
						|
    body_details.update(
 | 
						|
        {
 | 
						|
            "result": result,
 | 
						|
            "emoji": ":green_circle:" if result == "Passed" else ":red_circle:",
 | 
						|
            "end_time": pipeline_data["stage"]["last-transition-time"].tame(check_string),
 | 
						|
        }
 | 
						|
    )
 | 
						|
    body = COMPLETED_BODY_TEMPLATE.format(**body_details)
 | 
						|
 | 
						|
    body += get_jobs_details(pipeline_data)
 | 
						|
    return body
 |