webhooks: Add Netlify integration.

Fixes: #10169.
This commit is contained in:
Lenny Jagielski
2018-08-18 11:46:01 -05:00
committed by Tim Abbott
parent 8a9959b4be
commit 4fa4ca53c2
12 changed files with 348 additions and 0 deletions

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40"><defs><radialGradient id="a" cy="0%" r="100.11%" fx="50%" fy="0%" gradientTransform="matrix(0 .9989 -1.152 0 .5 -.5)"><stop offset="0%" stop-color="#20C6B7"/><stop offset="100%" stop-color="#4D9ABF"/></radialGradient></defs><path fill="url(#a)" d="M28.589 14.135l-.014-.006c-.008-.003-.016-.006-.023-.013a.11.11 0 0 1-.028-.093l.773-4.726 3.625 3.626-3.77 1.604a.083.083 0 0 1-.033.006h-.015a.104.104 0 0 1-.02-.017 1.716 1.716 0 0 0-.495-.381zm5.258-.288l3.876 3.876c.805.806 1.208 1.208 1.355 1.674.022.069.04.138.054.209l-9.263-3.923a.728.728 0 0 0-.015-.006c-.037-.015-.08-.032-.08-.07 0-.038.044-.056.081-.071l.012-.005 3.98-1.684zm5.127 7.003c-.2.376-.59.766-1.25 1.427l-4.37 4.369-5.652-1.177-.03-.006c-.05-.008-.103-.017-.103-.062a1.706 1.706 0 0 0-.655-1.193c-.023-.023-.017-.059-.01-.092 0-.005 0-.01.002-.014l1.063-6.526.004-.022c.006-.05.015-.108.06-.108a1.73 1.73 0 0 0 1.16-.665c.009-.01.015-.021.027-.027.032-.015.07 0 .103.014l9.65 4.082zm-6.625 6.801l-7.186 7.186 1.23-7.56.002-.01a.136.136 0 0 1 .006-.029c.01-.024.036-.034.061-.044l.012-.005a1.85 1.85 0 0 0 .695-.517c.024-.028.053-.055.09-.06a.09.09 0 0 1 .029 0l5.06 1.04zm-8.707 8.707l-.81.81-8.955-12.942a.424.424 0 0 0-.01-.014c-.014-.019-.029-.038-.026-.06.001-.016.011-.03.022-.042l.01-.013c.027-.04.05-.08.075-.123l.02-.035.003-.003c.014-.024.027-.047.051-.06.021-.01.05-.006.073-.001l9.921 2.046a.164.164 0 0 1 .076.033c.013.013.016.027.019.043a1.757 1.757 0 0 0 1.028 1.175c.028.014.016.045.003.078a.238.238 0 0 0-.015.045c-.125.76-1.197 7.298-1.485 9.063zm-1.692 1.691c-.597.591-.949.904-1.347 1.03a2 2 0 0 1-1.206 0c-.466-.148-.869-.55-1.674-1.356L8.73 28.73l2.349-3.643a.15.15 0 0 1 .04-.047c.025-.018.061-.01.091 0a2.434 2.434 0 0 0 1.638-.083c.027-.01.054-.017.075.002a.19.19 0 0 1 .028.032L21.95 38.05zM7.863 27.863L5.8 25.8l4.074-1.738a.084.084 0 0 1 .033-.007c.034 0 .054.034.072.065a2.91 2.91 0 0 0 .13.184l.013.016c.012.017.004.034-.008.05l-2.25 3.493zm-2.976-2.976l-2.61-2.61c-.444-.444-.766-.766-.99-1.043l7.936 1.646a.84.84 0 0 0 .03.005c.049.008.103.017.103.063 0 .05-.059.073-.109.092l-.023.01-4.337 1.837zM.831 19.892a2 2 0 0 1 .09-.495c.148-.466.55-.868 1.356-1.674l3.34-3.34a2175.525 2175.525 0 0 0 4.626 6.687c.027.036.057.076.026.106a2.776 2.776 0 0 0-.395.528.16.16 0 0 1-.05.062c-.013.008-.027.005-.042.002H9.78L.831 19.891zm5.68-6.403l4.491-4.491c.422.185 1.958.834 3.332 1.414 1.04.44 1.988.84 2.286.97.03.012.057.024.07.054.008.018.004.041 0 .06a2.003 2.003 0 0 0 .523 1.828c.03.03 0 .073-.026.11l-.014.021-4.56 7.063a.138.138 0 0 1-.043.05c-.024.015-.058.008-.086.001a2.274 2.274 0 0 0-.543-.074c-.164 0-.342.03-.522.063h-.001c-.02.003-.038.007-.054-.005a.21.21 0 0 1-.045-.051l-4.808-7.013zm5.398-5.398l5.814-5.814c.805-.805 1.208-1.208 1.674-1.355a2 2 0 0 1 1.206 0c.466.147.869.55 1.674 1.355l1.26 1.26-4.135 6.404a.155.155 0 0 1-.041.048c-.025.017-.06.01-.09 0a2.097 2.097 0 0 0-1.92.37c-.027.028-.067.012-.101-.003-.54-.235-4.74-2.01-5.341-2.265zm12.506-3.676l3.818 3.818-.92 5.698v.015a.135.135 0 0 1-.008.038c-.01.02-.03.024-.05.03a1.83 1.83 0 0 0-.548.273.154.154 0 0 0-.02.017c-.011.012-.022.023-.04.025a.114.114 0 0 1-.043-.007l-5.818-2.472-.011-.005c-.037-.015-.081-.033-.081-.071a2.198 2.198 0 0 0-.31-.915c-.028-.046-.059-.094-.035-.141l4.066-6.303zm-3.932 8.606l5.454 2.31c.03.014.063.027.076.058a.106.106 0 0 1 0 .057c-.016.08-.03.171-.03.263v.153c0 .038-.039.054-.075.069l-.011.004c-.864.369-12.13 5.173-12.147 5.173-.017 0-.035 0-.052-.017-.03-.03 0-.072.027-.11a.76.76 0 0 0 .014-.02l4.482-6.94.008-.012c.026-.042.056-.089.104-.089l.045.007c.102.014.192.027.283.027.68 0 1.31-.331 1.69-.897a.16.16 0 0 1 .034-.04c.027-.02.067-.01.098.004zm-6.246 9.185l12.28-5.237s.018 0 .035.017c.067.067.124.112.179.154l.027.017c.025.014.05.03.052.056 0 .01 0 .016-.002.025L25.756 23.7l-.004.026c-.007.05-.014.107-.061.107a1.729 1.729 0 0 0-1.373.847l-.005.008c-.014.023-.027.045-.05.057-.021.01-.048.006-.07.001l-9.793-2.02c-.01-.002-.152-.519-.163-.52z"/></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -329,6 +329,7 @@ WEBHOOK_INTEGRATIONS = [
WebhookIntegration('jira', ['project-management'], display_name='JIRA'),
WebhookIntegration('librato', ['monitoring']),
WebhookIntegration('mention', ['marketing'], display_name='Mention'),
WebhookIntegration('netlify', ['continuous-integration', 'deployment'], display_name='Netlify'),
WebhookIntegration('newrelic', ['monitoring'], display_name='New Relic'),
WebhookIntegration(
'opbeat',

View File

View File

@@ -0,0 +1,19 @@
Get Zulip notifications for your Netlify deployments!
1. {!create-stream.md!}
2. {!create-bot-construct-url-indented.md!}
3. Go to your Netlify project, and click **Settings**. Click **Build & deploy**, and select **Deploy notifications**.
Click **Add Notification**, and select **Outgoing webhook**.
4. Select an **Event**, and set **URL to notify** to the URL constructed above. Click **Save**.
{!congrats.md!}
![](/static/images/integrations/netlify/001.png)
!!! tip ""
For more information regarding Netlify webhooks, see: [Netlify's webhook documentation][1].
[1]: https://www.netlify.com/docs/webhooks/

View File

@@ -0,0 +1,45 @@
{
"id":"5b78192ac9659217dbf7c9aa",
"site_id":"573f11b2-f5f3-481f-a1f8-85feb457ff49",
"build_id":"5b78192ac9659217dbf7c9ab",
"state":"building",
"name":"objective-jepsen-35fbb2",
"url":"http://objective-jepsen-35fbb2.netlify.com",
"ssl_url":"https://objective-jepsen-35fbb2.netlify.com",
"admin_url":"https://app.netlify.com/sites/objective-jepsen-35fbb2",
"deploy_url":"http://5b78192ac9659217dbf7c9aa.objective-jepsen-35fbb2.netlify.com",
"deploy_ssl_url":"https://5b78192ac9659217dbf7c9aa--objective-jepsen-35fbb2.netlify.com",
"created_at":"2018-08-18T13:03:38.315Z",
"updated_at":"2018-08-18T13:03:39.800Z",
"user_id":"5b68ca02b13fb17905788d44",
"error_message":null,
"required":[
],
"required_functions":null,
"commit_ref":null,
"review_id":null,
"branch":"master",
"commit_url":null,
"skipped":null,
"locked":null,
"log_access_attributes":{
"type":"firebase",
"url":"https://netlify.firebaseio.com/builds/5b78192ac9659217dbf7c9ab/log"
},
"title":null,
"review_url":null,
"published_at":null,
"context":"production",
"deploy_time":null,
"available_functions":[
],
"summary":{
"status":"building",
"messages":[
]
},
"screenshot_url":null
}

View File

@@ -0,0 +1,52 @@
{
"id":"5b78192ac9659217dbf7c9aa",
"site_id":"573f11b2-f5f3-481f-a1f8-85feb457ff49",
"build_id":"5b78192ac9659217dbf7c9ab",
"state":"ready",
"name":"objective-jepsen-35fbb2",
"url":"http://objective-jepsen-35fbb2.netlify.com",
"ssl_url":"https://objective-jepsen-35fbb2.netlify.com",
"admin_url":"https://app.netlify.com/sites/objective-jepsen-35fbb2",
"deploy_url":"http://5b78192ac9659217dbf7c9aa.objective-jepsen-35fbb2.netlify.com",
"deploy_ssl_url":"https://5b78192ac9659217dbf7c9aa--objective-jepsen-35fbb2.netlify.com",
"created_at":"2018-08-18T13:03:38.315Z",
"updated_at":"2018-08-18T13:03:44.609Z",
"user_id":"5b68ca02b13fb17905788d44",
"error_message":null,
"required":[
],
"required_functions":[
],
"commit_ref":null,
"review_id":null,
"branch":"master",
"commit_url":null,
"skipped":null,
"locked":null,
"log_access_attributes":{
"type":"firebase",
"url":"https://netlify.firebaseio.com/builds/5b78192ac9659217dbf7c9ab/log"
},
"title":null,
"review_url":null,
"published_at":"2018-08-18T13:03:42.462Z",
"context":"production",
"deploy_time":3,
"available_functions":[
],
"summary":{
"status":"ready",
"messages":[
{
"type":"info",
"title":"No new files created for this deploy",
"description":null,
"image":null
}
]
},
"screenshot_url":"https://353a23c500dde3b2ad58-c49fe7e7355d384845270f4a7a0a7aa1.ssl.cf2.rackcdn.com/5b78192ac9659217dbf7c9aa/screenshot.png"
}

View File

@@ -0,0 +1,41 @@
{
"id":"5b64d77c02ed83730664c2f6",
"site_id":"6f2ad239-fce7-4b54-81fe-873d4fcf5c78",
"build_id":"5b64d77c02ed83730664c2f7",
"state":"error",
"name":"objective-jepsen-35fbb2",
"url":"http://objective-jepsen-35fbb2.netlify.com",
"ssl_url":"https://objective-jepsen-35fbb2.netlify.com",
"admin_url":"https://app.netlify.com/sites/objective-jepsen-35fbb2",
"deploy_url":"http://5b78192ac9659217dbf7c9aa.objective-jepsen-35fbb2.netlify.com",
"deploy_ssl_url":"https://5b78192ac9659217dbf7c9aa--objective-jepsen-35fbb2.netlify.com",
"created_at":"2018-08-03T22:30:20.261Z",
"updated_at":"2018-08-03T22:30:27.734Z",
"user_id":"5b64d15c82d3f16bcbbdcdbe",
"error_message":"failed during stage 'building site': Build script returned non-zero exit code: 127",
"required":[
],
"required_functions":null,
"commit_ref":null,
"review_id":null,
"branch":"master",
"commit_url":null,
"skipped":null,
"locked":null,
"log_access_attributes":{
"type":"firebase",
"url":"https://netlify.firebaseio.com/builds/5b64d77c02ed83730664c2f7/log"
},
"title":null,
"review_url":null,
"published_at":null,
"context":"production",
"deploy_time":null,
"available_functions":[
],
"summary":{
}
}

View File

@@ -0,0 +1,43 @@
{
"id":"5b64d2c3792f8946ae9ddc8b",
"site_id":"6f2ad239-fce7-4b54-81fe-873d4fcf5c78",
"build_id":"5b64d2c3792f8946ae9ddc8c",
"state":"ready",
"name":"objective-jepsen-35fbb2",
"url":"http://objective-jepsen-35fbb2.netlify.com",
"ssl_url":"https://objective-jepsen-35fbb2.netlify.com",
"admin_url":"https://app.netlify.com/sites/objective-jepsen-35fbb2",
"deploy_url":"http://5b78192ac9659217dbf7c9aa.objective-jepsen-35fbb2.netlify.com",
"deploy_ssl_url":"https://5b78192ac9659217dbf7c9aa--objective-jepsen-35fbb2.netlify.com",
"created_at":"2018-08-03T22:10:11.180Z",
"updated_at":"2018-08-03T22:21:59.875Z",
"user_id":"5b64d15c82d3f16bcbbdcdbe",
"error_message":null,
"required":[
],
"required_functions":[
],
"commit_ref":null,
"review_id":null,
"branch":"master",
"commit_url":null,
"skipped":null,
"locked":true,
"log_access_attributes":{
"type":"firebase",
"url":"https://netlify.firebaseio.com/builds/5b64d2c3792f8946ae9ddc8c/log"
},
"title":null,
"review_url":null,
"published_at":"2018-08-03T22:21:59.747Z",
"context":"production",
"deploy_time":2,
"available_functions":[
],
"summary":{
}
}

View File

@@ -0,0 +1,43 @@
{
"id":"5b64d2c3792f8946ae9ddc8b",
"site_id":"6f2ad239-fce7-4b54-81fe-873d4fcf5c78",
"build_id":"5b64d2c3792f8946ae9ddc8c",
"state":"ready",
"name":"objective-jepsen-35fbb2",
"url":"http://objective-jepsen-35fbb2.netlify.com",
"ssl_url":"https://objective-jepsen-35fbb2.netlify.com",
"admin_url":"https://app.netlify.com/sites/objective-jepsen-35fbb2",
"deploy_url":"http://5b78192ac9659217dbf7c9aa.objective-jepsen-35fbb2.netlify.com",
"deploy_ssl_url":"https://5b78192ac9659217dbf7c9aa--objective-jepsen-35fbb2.netlify.com",
"created_at":"2018-08-03T22:10:11.180Z",
"updated_at":"2018-08-03T22:24:04.622Z",
"user_id":"5b64d15c82d3f16bcbbdcdbe",
"error_message":null,
"required":[
],
"required_functions":[
],
"commit_ref":null,
"review_id":null,
"branch":"master",
"commit_url":null,
"skipped":null,
"locked":false,
"log_access_attributes":{
"type":"firebase",
"url":"https://netlify.firebaseio.com/builds/5b64d2c3792f8946ae9ddc8c/log"
},
"title":null,
"review_url":null,
"published_at":"2018-08-03T22:21:59.747Z",
"context":"production",
"deploy_time":2,
"available_functions":[
],
"summary":{
}
}

View File

@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
from django.conf import settings
from zerver.lib.test_classes import WebhookTestCase
from zerver.models import get_system_bot
class NetlifyHookTests(WebhookTestCase):
STREAM_NAME = 'netlify'
URL_TEMPLATE = u"/api/v1/external/netlify?stream={stream}&api_key={api_key}"
def test_building_message(self) -> None:
expected_subject = u"master"
expected_message = u'The build [objective-jepsen-35fbb2](http://objective-jepsen-35fbb2.netlify.com) on branch master is now building.'
self.send_and_test_stream_message('deploy_building', expected_subject, expected_message,
content_type="application/json", HTTP_X_NETLIFY_EVENT='deploy_building')
def test_created_message(self) -> None:
expected_subject = u"master"
expected_message = u'The build [objective-jepsen-35fbb2](http://objective-jepsen-35fbb2.netlify.com) on branch master is now ready.'
self.send_and_test_stream_message('deploy_created', expected_subject, expected_message,
content_type="application/json", HTTP_X_NETLIFY_EVENT='deploy_created')
def test_failed_message(self) -> None:
expected_subject = u"master"
expected_message = (u"The build [objective-jepsen-35fbb2](http://objective-jepsen-35fbb2.netlify.com) "
u"on branch master failed during stage 'building site': Build script returned non-zero exit code: 127"
)
self.send_and_test_stream_message('deploy_failed', expected_subject, expected_message,
content_type="application/json", HTTP_X_NETLIFY_EVENT='deploy_failed')
def test_locked_message(self) -> None:
expected_subject = u"master"
expected_message = (u"The build [objective-jepsen-35fbb2](http://objective-jepsen-35fbb2.netlify.com) "
u"on branch master is now locked."
)
self.send_and_test_stream_message('deploy_locked', expected_subject, expected_message,
content_type="application/json", HTTP_X_NETLIFY_EVENT='deploy_locked')
def test_unlocked_message(self) -> None:
expected_subject = u"master"
expected_message = (u"The build [objective-jepsen-35fbb2](http://objective-jepsen-35fbb2.netlify.com) "
u"on branch master is now unlocked."
)
self.send_and_test_stream_message('deploy_unlocked', expected_subject, expected_message,
content_type="application/json", HTTP_X_NETLIFY_EVENT='deploy_unlocked')
def get_body(self, fixture_name: str) -> str:
return self.webhook_fixture_data("netlify", fixture_name, file_type="json")

View File

@@ -0,0 +1,50 @@
from typing import Any, Dict, Iterable, Optional
from django.http import HttpRequest, HttpResponse
from django.utils.translation import ugettext as _
from zerver.decorator import api_key_only_webhook_view
from zerver.lib.webhooks.common import check_send_webhook_message, \
validate_extract_webhook_http_header, UnexpectedWebhookEventType
from zerver.lib.request import REQ, has_request_variables
from zerver.lib.response import json_error, json_success
from zerver.lib.validator import check_dict, check_string
from zerver.models import UserProfile
EVENTS = ['deploy_failed', 'deploy_locked', 'deploy_unlocked', 'deploy_building', 'deploy_created']
@api_key_only_webhook_view('Netlify')
@has_request_variables
def api_netlify_webhook(
request: HttpRequest, user_profile: UserProfile,
payload: Dict[str, Iterable[Dict[str, Any]]]=REQ(argument_type='body')
) -> HttpResponse:
message_template = get_template(request, payload)
body = message_template.format(build_name=payload['name'],
build_url=payload['url'],
branch_name=payload['branch'],
state=payload['state'])
topic = "{topic}".format(topic=payload['branch'])
check_send_webhook_message(request, user_profile, topic, body)
return json_success()
def get_template(request: HttpRequest, payload: Dict[str, Any]) -> str:
message_template = u'The build [{build_name}]({build_url}) on branch {branch_name} '
event = validate_extract_webhook_http_header(request, 'X_NETLIFY_EVENT', 'Netlify')
if event == 'deploy_failed':
return message_template + payload['error_message']
elif event == 'deploy_locked':
return message_template + 'is now locked.'
elif event == 'deploy_unlocked':
return message_template + 'is now unlocked.'
elif event in EVENTS:
return message_template + 'is now {state}.'.format(state=payload['state'])
else:
raise UnexpectedWebhookEventType('Netlify', event)