snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-15 15:05:30 +05:00
commit 64898b2c79
8999 changed files with 1757358 additions and 0 deletions

View File

View File

@@ -0,0 +1,50 @@
# Zulip TeamCity integration
Get Zulip notifications for your TeamCity builds!
{start_tabs}
1. {!create-an-incoming-webhook.md!}
1. {!generate-webhook-url-basic.md!}
1. Install the [tcWebHooks plugin][1] onto your TeamCity server. Follow
the plugin instructions in your TeamCity documentation, or refer to
[the online TeamCity documentation][2].
1. Go to your TeamCity **Overview** page. Select the **Project** or
**Build** you'd like to receive notifications about, and click on the
**WebHooks** tab. Click **Add project WebHooks** for a **Project**,
or click **Add build WebHooks** for a **Build**. Select **Click to
create new WebHook for this project/build**.
1. Set **URL** to the URL generated above, and set **Payload Format** to
**Legacy Webhook (JSON)**. Untoggle all **Trigger on Events** options,
and toggle **Trigger when build is Successful** and **Trigger when
build Fails**. You may also toggle the options **Only trigger when
build changes from Failure to Success** and **Only trigger when build
changes from Success to Failure** if you'd like. Click **Save**.
{end_tabs}
{!congrats.md!}
![](/static/images/integrations/teamcity/001.png)
### Personal Builds
When a user runs a personal build in TeamCity, if Zulip can map their
TeamCity username to a Zulip user (by matching it to a Zulip user's
email address or full name), then that Zulip user will receive a direct
message with the result of their personal build.
### Related documentation
- [tcWebHooks plugin][1]
- [TeamCity plugin installation documentation][2]
{!webhooks-url-specification.md!}
[1]: https://github.com/tcplugins/tcWebHooks/releases
[2]: https://www.jetbrains.com/help/teamcity/installing-additional-plugins.html

View File

@@ -0,0 +1,53 @@
{
"build": {
"buildStatus": "Exit code 1 (new)",
"buildResult": "failure",
"buildResultPrevious": "success",
"buildResultDelta": "broken",
"notifyType": "buildFinished",
"buildFullName": "Project :: Compile",
"buildName": "Compile",
"buildId": "19952",
"buildTypeId": "Project_Compile",
"buildInternalTypeId": "bt1",
"buildExternalTypeId": "Project_Compile",
"buildStatusUrl": "http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952",
"buildStatusHtml": "<span class=\"tcWebHooksMessage\"><a href=\"http://teamcity/project.html?projectId=Project\">Project</a> :: <a href=\"http://teamcity/viewType.html?buildTypeId=Project_Compile\">Compile</a> # <a href=\"http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952\"><strong>5535 - CL 123456</strong></a> has <strong>finished</strong> with a status of <a href=\"http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952\"> <strong>failure</strong></a> and was triggered by <strong>Perforce</strong></span>",
"rootUrl": "http://teamcity",
"projectName": "Project",
"projectId": "Project",
"projectInternalId": "project1",
"projectExternalId": "Project",
"buildNumber": "5535 - CL 123456",
"agentName": "agent123",
"agentOs": "Windows 10, version 10.0",
"agentHostname": "agent123.company.com",
"triggeredBy": "Perforce",
"message": "Build Project :: Compile has finished. This is build number 5535 - CL 123456, has a status of \"failure\" and was triggered by Perforce",
"text": "Project :: Compile has finished. Status: failure",
"buildStateDescription": "finished",
"buildRunners": [
"Command Line",
"Command Line",
"Command Line",
"Command Line",
"Command Line",
"Command Line"
],
"extraParameters": [ ],
"teamcityProperties": [
{
"name": "DotNetFramework2.0.50727_x64_Path",
"value": "C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727"
},
{
"name": "DotNetFramework2.0.50727_x86_Path",
"value": "C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727"
},
{
"name": "DotNetFramework2.0_x64",
"value": "2.0.50727"
}
]
}
}

View File

@@ -0,0 +1,53 @@
{
"build": {
"buildStatus": "Exit code 1",
"buildResult": "failure",
"buildResultPrevious": "failure",
"buildResultDelta": "unchanged",
"notifyType": "buildFinished",
"buildFullName": "Project :: Compile",
"buildName": "Compile",
"buildId": "19952",
"buildTypeId": "Project_Compile",
"buildInternalTypeId": "bt1",
"buildExternalTypeId": "Project_Compile",
"buildStatusUrl": "http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952",
"buildStatusHtml": "<span class=\"tcWebHooksMessage\"><a href=\"http://teamcity/project.html?projectId=Project\">Project</a> :: <a href=\"http://teamcity/viewType.html?buildTypeId=Project_Compile\">Compile</a> # <a href=\"http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952\"><strong>5535 - CL 123456</strong></a> has <strong>finished</strong> with a status of <a href=\"http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952\"> <strong>failure</strong></a> and was triggered by <strong>Perforce</strong></span>",
"rootUrl": "http://teamcity",
"projectName": "Project",
"projectId": "Project",
"projectInternalId": "project1",
"projectExternalId": "Project",
"buildNumber": "5535 - CL 123456",
"agentName": "agent123",
"agentOs": "Windows 10, version 10.0",
"agentHostname": "agent123.company.com",
"triggeredBy": "Perforce",
"message": "Build Project :: Compile has finished. This is build number 5535 - CL 123456, has a status of \"failure\" and was triggered by Perforce",
"text": "Project :: Compile has finished. Status: failure",
"buildStateDescription": "finished",
"buildRunners": [
"Command Line",
"Command Line",
"Command Line",
"Command Line",
"Command Line",
"Command Line"
],
"extraParameters": [ ],
"teamcityProperties": [
{
"name": "DotNetFramework2.0.50727_x64_Path",
"value": "C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727"
},
{
"name": "DotNetFramework2.0.50727_x86_Path",
"value": "C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727"
},
{
"name": "DotNetFramework2.0_x64",
"value": "2.0.50727"
}
]
}
}

View File

@@ -0,0 +1,53 @@
{
"build": {
"buildStatus": "Tests passed: 27",
"buildResult": "success",
"buildResultPrevious": "failure",
"buildResultDelta": "fixed",
"notifyType": "buildFinished",
"buildFullName": "Project :: Compile",
"buildName": "Compile",
"buildId": "19952",
"buildTypeId": "Project_Compile",
"buildInternalTypeId": "bt1",
"buildExternalTypeId": "Project_Compile",
"buildStatusUrl": "http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952",
"buildStatusHtml": "<span class=\"tcWebHooksMessage\"><a href=\"http://teamcity/project.html?projectId=Project\">Project</a> :: <a href=\"http://teamcity/viewType.html?buildTypeId=Project_Compile\">Compile</a> # <a href=\"http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952\"><strong>5535 - CL 123456</strong></a> has <strong>finished</strong> with a status of <a href=\"http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952\"> <strong>success</strong></a> and was triggered by <strong>Perforce</strong></span>",
"rootUrl": "http://teamcity",
"projectName": "Project",
"projectId": "Project",
"projectInternalId": "project1",
"projectExternalId": "Project",
"buildNumber": "5535 - CL 123456",
"agentName": "agent123",
"agentOs": "Windows 10, version 10.0",
"agentHostname": "agent123.company.com",
"triggeredBy": "Perforce",
"message": "Build Project :: Compile has finished. This is build number 5535 - CL 123456, has a status of \"success\" and was triggered by Perforce",
"text": "Project :: Compile has finished. Status: success",
"buildStateDescription": "finished",
"buildRunners": [
"Command Line",
"Command Line",
"Command Line",
"Command Line",
"Command Line",
"Command Line"
],
"extraParameters": [ ],
"teamcityProperties": [
{
"name": "DotNetFramework2.0.50727_x64_Path",
"value": "C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727"
},
{
"name": "DotNetFramework2.0.50727_x86_Path",
"value": "C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727"
},
{
"name": "DotNetFramework2.0_x64",
"value": "2.0.50727"
}
]
}
}

View File

@@ -0,0 +1,61 @@
{
"build": {
"buildStatus": "Exit code 1 (new)",
"buildResult": "failure",
"buildResultPrevious": "success",
"buildResultDelta": "broken",
"notifyType": "buildFinished",
"buildFullName": "Project :: Compile",
"buildName": "Compile",
"buildId": "19952",
"buildTypeId": "Project_Compile",
"buildInternalTypeId": "bt1",
"buildExternalTypeId": "Project_Compile",
"buildStatusUrl": "http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952",
"buildStatusHtml": "<span class=\"tcWebHooksMessage\"><a href=\"http://teamcity/project.html?projectId=Project\">Project</a> :: <a href=\"http://teamcity/viewType.html?buildTypeId=Project_Compile\">Compile</a> # <a href=\"http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952\"><strong>5535 - CL 123456</strong></a> has <strong>finished</strong> with a status of <a href=\"http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952\"> <strong>failure</strong></a> and was triggered by <strong>David Payne; MS Visual Studio</strong></span>",
"rootUrl": "http://teamcity",
"projectName": "Project",
"projectId": "Project",
"projectInternalId": "project1",
"projectExternalId": "Project",
"buildNumber": "5535 - CL 123456",
"agentName": "agent123",
"agentOs": "Windows 10, version 10.0",
"agentHostname": "agent123.company.com",
"triggeredBy": "David Payne; MS Visual Studio",
"message": "Build Project :: Compile has finished. This is build number 5535 - CL 123456, has a status of \"failure\" and was triggered by David Payne; MS Visual Studio",
"text": "Project :: Compile has finished. Status: failure",
"buildStateDescription": "finished",
"buildRunners": [
"Command Line",
"Command Line",
"Command Line",
"Command Line",
"Command Line",
"Command Line"
],
"extraParameters": [ ],
"teamcityProperties": [
{
"name": "DotNetFramework2.0.50727_x64_Path",
"value": "C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727"
},
{
"name": "DotNetFramework2.0.50727_x86_Path",
"value": "C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727"
},
{
"name": "DotNetFramework2.0_x64",
"value": "2.0.50727"
},
{
"name": "env.BUILD_IS_PERSONAL",
"value": "true"
},
{
"name": "teamcity.build.triggeredBy.username",
"value": "iago"
}
]
}
}

View File

@@ -0,0 +1,42 @@
{
"username":"TeamCity",
"icon_url":"https://raw.githubusercontent.com/tcplugins/tcWebHooks/master/docs/icons/teamcity-logo-48x48.png",
"attachments":[
{
"title":"Failed (broken) : ${buildName} &lt;${buildStatusUrl}|build #${buildNumber}&gt;",
"fallback":"Failed (broken) : ${buildName} build #${buildNumber}",
"color":"danger",
"fields":[
{
"title":"Status",
"value":"${buildStatus}"
},
{
"title":"Project Name",
"value":"&lt;${rootUrl}/project.html?projectId=${projectExternalId}|${projectName}&gt;",
"short":true
},
{
"title":"Build Name",
"value":"&lt;${rootUrl}/viewType.html?buildTypeId=${buildExternalTypeId}|${buildName}&gt;",
"short":true
},
{
"title":"Commit",
"value":"&lt;${buildStatusUrl}&amp;tab=buildChangesDiv|${substr(build.vcs.number,0,7,32)}&gt;",
"short":true
},
{
"title":"Triggered By",
"value":"${triggeredBy}",
"short":true
},
{
"title":"Agent",
"value":"${agentName}",
"short":true
}
]
}
]
}

View File

@@ -0,0 +1,53 @@
{
"build": {
"buildStatus": "Tests passed: 27",
"buildResult": "success",
"buildResultPrevious": "success",
"buildResultDelta": "unchanged",
"notifyType": "buildFinished",
"buildFullName": "Project :: Compile",
"buildName": "Compile",
"buildId": "19952",
"buildTypeId": "Project_Compile",
"buildInternalTypeId": "bt1",
"buildExternalTypeId": "Project_Compile",
"buildStatusUrl": "http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952",
"buildStatusHtml": "<span class=\"tcWebHooksMessage\"><a href=\"http://teamcity/project.html?projectId=Project\">Project</a> :: <a href=\"http://teamcity/viewType.html?buildTypeId=Project_Compile\">Compile</a> # <a href=\"http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952\"><strong>5535 - CL 123456</strong></a> has <strong>finished</strong> with a status of <a href=\"http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952\"> <strong>success</strong></a> and was triggered by <strong>Perforce</strong></span>",
"rootUrl": "http://teamcity",
"projectName": "Project",
"projectId": "Project",
"projectInternalId": "project1",
"projectExternalId": "Project",
"buildNumber": "5535 - CL 123456",
"agentName": "agent123",
"agentOs": "Windows 10, version 10.0",
"agentHostname": "agent123.company.com",
"triggeredBy": "Perforce",
"message": "Build Project :: Compile has finished. This is build number 5535 - CL 123456, has a status of \"success\" and was triggered by Perforce",
"text": "Project :: Compile has finished. Status: success",
"buildStateDescription": "finished",
"buildRunners": [
"Command Line",
"Command Line",
"Command Line",
"Command Line",
"Command Line",
"Command Line"
],
"extraParameters": [ ],
"teamcityProperties": [
{
"name": "DotNetFramework2.0.50727_x64_Path",
"value": "C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727"
},
{
"name": "DotNetFramework2.0.50727_x86_Path",
"value": "C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727"
},
{
"name": "DotNetFramework2.0_x64",
"value": "2.0.50727"
}
]
}
}

View File

@@ -0,0 +1,52 @@
{
"build": {
"buildStatus": "Tests passed: 27",
"buildResult": "success",
"buildResultPrevious": "success",
"buildResultDelta": "unchanged",
"notifyType": "buildFinished",
"buildFullName": "Project :: Compile",
"buildName": "Compile",
"buildId": "19952",
"buildTypeId": "Project_Compile",
"buildInternalTypeId": "bt1",
"buildExternalTypeId": "Project_Compile",
"buildStatusUrl": "http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952",
"buildStatusHtml": "<span class=\"tcWebHooksMessage\"><a href=\"http://teamcity/project.html?projectId=Project\">Project</a> :: <a href=\"http://teamcity/viewType.html?buildTypeId=Project_Compile\">Compile</a> # <a href=\"http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952\"><strong>5535 - CL 123456</strong></a> has <strong>finished</strong> with a status of <a href=\"http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952\"> <strong>success</strong></a> and was triggered by <strong>Perforce</strong></span>",
"rootUrl": "http://teamcity",
"projectName": "Project",
"projectId": "Project",
"projectInternalId": "project1",
"projectExternalId": "Project",
"branchName": "MyBranch",
"branchDisplayName": "MyBranch",
"buildNumber": "5535 - CL 123456",
"agentName": "agent123",
"agentOs": "Windows 10, version 10.0",
"agentHostname": "agent123.company.com",
"triggeredBy": "Perforce",
"message": "Build Project :: Compile has finished. This is build number 5535 - CL 123456, has a status of \"success\" and was triggered by Perforce",
"text": "Project :: Compile has finished. Status: success",
"buildStateDescription": "finished",
"buildRunners": [
"Command Line",
"Command Line",
"Command Line",
"Command Line",
"Command Line",
"Command Line"
],
"extraParameters": [],
"teamcityProperties": [{
"name": "DotNetFramework2.0.50727_x64_Path",
"value": "C:\\Windows\\Microsoft.NET\\Framework64\\v2.0.50727"
}, {
"name": "DotNetFramework2.0.50727_x86_Path",
"value": "C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727"
}, {
"name": "DotNetFramework2.0_x64",
"value": "2.0.50727"
}
]
}
}

View File

@@ -0,0 +1,58 @@
import orjson
from zerver.lib.send_email import FromAddress
from zerver.lib.test_classes import WebhookTestCase
from zerver.models import Recipient
from zerver.models.realms import get_realm
from zerver.models.users import get_user_by_delivery_email
from zerver.webhooks.teamcity.view import MISCONFIGURED_PAYLOAD_TYPE_ERROR_MESSAGE
class TeamCityHookTests(WebhookTestCase):
TOPIC_NAME = "Project :: Compile"
def test_teamcity_success(self) -> None:
expected_message = "Project :: Compile build 5535 - CL 123456 was successful! :thumbs_up: See [changes](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952&tab=buildChangesDiv) and [build log](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952)."
self.check_webhook("success", self.TOPIC_NAME, expected_message)
def test_teamcity_success_branch(self) -> None:
expected_message = "Project :: Compile build 5535 - CL 123456 was successful! :thumbs_up: See [changes](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952&tab=buildChangesDiv) and [build log](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952)."
expected_topic_name = "Project :: Compile (MyBranch)"
self.check_webhook("success_branch", expected_topic_name, expected_message)
def test_teamcity_broken(self) -> None:
expected_message = "Project :: Compile build 5535 - CL 123456 is broken with status Exit code 1 (new)! :thumbs_down: See [changes](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952&tab=buildChangesDiv) and [build log](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952)."
self.check_webhook("broken", self.TOPIC_NAME, expected_message)
def test_teamcity_failure(self) -> None:
expected_message = "Project :: Compile build 5535 - CL 123456 is still broken with status Exit code 1! :thumbs_down: See [changes](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952&tab=buildChangesDiv) and [build log](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952)."
self.check_webhook("failure", self.TOPIC_NAME, expected_message)
def test_teamcity_fixed(self) -> None:
expected_message = "Project :: Compile build 5535 - CL 123456 has been fixed! :thumbs_up: See [changes](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952&tab=buildChangesDiv) and [build log](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952)."
self.check_webhook("fixed", self.TOPIC_NAME, expected_message)
def test_teamcity_personal(self) -> None:
expected_message = "Your personal build for Project :: Compile build 5535 - CL 123456 is broken with status Exit code 1 (new)! :thumbs_down: See [changes](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952&tab=buildChangesDiv) and [build log](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952)."
payload = orjson.dumps(
orjson.loads(self.webhook_fixture_data(self.webhook_dir_name, "personal"))
)
self.client_post(self.url, payload, content_type="application/json")
msg = self.get_last_message()
self.assertEqual(msg.content, expected_message)
self.assertEqual(msg.recipient.type, Recipient.DIRECT_MESSAGE_GROUP)
def test_non_generic_payload_ignore_pm_notification(self) -> None:
expected_message = MISCONFIGURED_PAYLOAD_TYPE_ERROR_MESSAGE.format(
bot_name=get_user_by_delivery_email(
"webhook-bot@zulip.com", get_realm("zulip")
).full_name,
support_email=FromAddress.SUPPORT,
).strip()
payload = self.get_body("slack_non_generic_payload")
self.client_post(self.url, payload, content_type="application/json")
msg = self.get_last_message()
self.assertEqual(msg.content, expected_message)
self.assertEqual(msg.recipient.type, Recipient.DIRECT_MESSAGE_GROUP)

View File

@@ -0,0 +1,151 @@
# Webhooks for teamcity integration
import logging
from django.db.models import Q
from django.http import HttpRequest, HttpResponse
from zerver.actions.message_send import (
check_send_private_message,
send_rate_limited_pm_notification_to_bot_owner,
)
from zerver.decorator import webhook_view
from zerver.lib.request import RequestNotes
from zerver.lib.response import json_success
from zerver.lib.send_email import FromAddress
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.models import Realm, UserProfile
MISCONFIGURED_PAYLOAD_TYPE_ERROR_MESSAGE = """
Hi there! Your bot {bot_name} just received a TeamCity payload in a
format that Zulip doesn't recognize. This usually indicates a
configuration issue in your TeamCity webhook settings. Please make sure
that you set the **Payload Format** option to **Legacy Webhook (JSON)**
in your TeamCity webhook configuration. Contact {support_email} if you
need further help!
"""
def guess_zulip_user_from_teamcity(teamcity_username: str, realm: Realm) -> UserProfile | None:
try:
# Try to find a matching user in Zulip
# We search a user's full name, short name,
# and beginning of email address
user = UserProfile.objects.filter(
Q(full_name__iexact=teamcity_username) | Q(email__istartswith=teamcity_username),
is_active=True,
realm=realm,
).order_by("id")[0]
return user
except IndexError:
return None
def get_teamcity_property_value(property_list: WildValue, name: str) -> str | None:
for property in property_list:
if property["name"].tame(check_string) == name:
return property["value"].tame(check_string)
return None
@webhook_view("TeamCity")
@typed_endpoint
def api_teamcity_webhook(
request: HttpRequest,
user_profile: UserProfile,
*,
payload: JsonBodyPayload[WildValue],
) -> HttpResponse:
if "build" not in payload:
# Ignore third-party specific (e.g. Slack) payload formats
# and notify the bot owner
error_message = MISCONFIGURED_PAYLOAD_TYPE_ERROR_MESSAGE.format(
bot_name=user_profile.full_name,
support_email=FromAddress.SUPPORT,
).strip()
send_rate_limited_pm_notification_to_bot_owner(
user_profile, user_profile.realm, error_message
)
return json_success(request)
message = payload.get("build")
build_name = message["buildFullName"].tame(check_string)
build_url = message["buildStatusUrl"].tame(check_string)
changes_url = build_url + "&tab=buildChangesDiv"
build_number = message["buildNumber"].tame(check_string)
build_result = message["buildResult"].tame(check_string)
build_result_delta = message["buildResultDelta"].tame(check_string)
build_status = message["buildStatus"].tame(check_string)
if build_result == "success":
if build_result_delta == "fixed":
status = "has been fixed! :thumbs_up:"
else:
status = "was successful! :thumbs_up:"
elif build_result == "failure":
if build_result_delta == "broken":
status = f"is broken with status {build_status}! :thumbs_down:"
else:
status = f"is still broken with status {build_status}! :thumbs_down:"
elif build_result == "running":
status = "has started."
template = """
{build_name} build {build_id} {status} See [changes]\
({changes_url}) and [build log]({log_url}).
""".strip()
body = template.format(
build_name=build_name,
build_id=build_number,
status=status,
changes_url=changes_url,
log_url=build_url,
)
if "branchDisplayName" in message:
topic_name = "{} ({})".format(build_name, message["branchDisplayName"].tame(check_string))
else:
topic_name = build_name
# Check if this is a personal build, and if so try to direct message the user who triggered it.
if (
get_teamcity_property_value(message["teamcityProperties"], "env.BUILD_IS_PERSONAL")
== "true"
):
# The triggeredBy field gives us the teamcity user full name, and the
# "teamcity.build.triggeredBy.username" property gives us the teamcity username.
# Let's try finding the user email from both.
teamcity_fullname = message["triggeredBy"].tame(check_string).split(";")[0]
teamcity_user = guess_zulip_user_from_teamcity(teamcity_fullname, user_profile.realm)
if teamcity_user is None:
teamcity_shortname = get_teamcity_property_value(
message["teamcityProperties"], "teamcity.build.triggeredBy.username"
)
if teamcity_shortname is not None:
teamcity_user = guess_zulip_user_from_teamcity(
teamcity_shortname, user_profile.realm
)
if teamcity_user is None:
# We can't figure out who started this build - there's nothing we can do here.
logging.info(
"TeamCity webhook couldn't find a matching Zulip user for "
"TeamCity user '%s' or '%s'",
teamcity_fullname,
teamcity_shortname,
)
return json_success(request)
body = f"Your personal build for {body}"
client = RequestNotes.get_notes(request).client
assert client is not None
check_send_private_message(user_profile, client, teamcity_user, body)
return json_success(request)
check_send_webhook_message(request, user_profile, topic_name, body)
return json_success(request)