diff --git a/api/integrations/git/humbug_git_config.py b/api/integrations/git/humbug_git_config.py new file mode 100644 index 0000000000..f505d5b66a --- /dev/null +++ b/api/integrations/git/humbug_git_config.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright © 2013 Humbug, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + + +# Change these values to configure authentication for the plugin +HUMBUG_USER = "git@example.com" +HUMBUG_API_KEY = "0123456789abcdef0123456789abcdef" + +# commit_notice_destination() lets you customize where commit notices +# are sent to with the full power of a Python function. +# +# It takes the following arguments: +# * repo = the name of the git repository +# * branch = the name of the branch that was pushed to +# * commit = the commit id +# +# Returns a dictionary encoding the stream and subject to send the +# notification to (or None to send no notification). +# +# The default code below will send every commit pushed to "master" to +# * stream "commits" +# * subject "deploy => master" (using a pretty unicode right arrow) +# And similarly for branch "test-post-receive" (for use when testing). +def commit_notice_destination(repo, branch, commit): + if branch in ["master", "test-post-receive"]: + return dict(stream = "commits", + subject = u"deploy \u21D2 %s" % (branch,)) + + # Return None for cases where you don't want a notice sent + return None + +## If properly installed, the Humbug API should be in your import +## path, but if not, set a custom path below +HUMBUG_API_PATH = None + +# This should not need to change unless you have a custom Humbug subdomain. +HUMBUG_SITE = "https://humbughq.com" diff --git a/api/integrations/git/post-receive b/api/integrations/git/post-receive new file mode 100755 index 0000000000..6d8ecb60d4 --- /dev/null +++ b/api/integrations/git/post-receive @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Humbug notification post-receive hook. +# Copyright © 2012-2013 Humbug, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# The "post-receive" script is run after receive-pack has accepted a pack +# and the repository has been updated. It is passed arguments in through +# stdin in the form +# +# For example: +# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master + +import os +import sys +import subprocess +import time +import os.path + +sys.path.insert(0, os.path.dirname(__file__)) +import humbug_git_config as config + +if config.HUMBUG_API_PATH is not None: + sys.path.append(config.HUMBUG_API_PATH) + +import humbug +client = humbug.Client( + email=config.HUMBUG_USER, + site=config.HUMBUG_SITE, + api_key=config.HUMBUG_API_KEY) + +# check_output is backported from subprocess.py in Python 2.7 +def check_output(*popenargs, **kwargs): + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be overridden.') + process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + raise subprocess.CalledProcessError(retcode, cmd, output=output) + return output +subprocess.check_output = check_output + +def git_repository_name(): + output = subprocess.check_output(["git", "rev-parse", "--is-bare-repository"]) + if output.strip() == "true": + return os.path.basename(os.getcwd())[:-len(".git")] + else: + return os.path.basename(os.path.dirname(os.getcwd())) + +def git_commit_range(oldrev, newrev): + log_cmd = ["git", "log", "--reverse", + "--pretty=%aE %s", "%s..%s" % (oldrev, newrev)] + commits = '' + for ln in subprocess.check_output(log_cmd).splitlines(): + email, subject = ln.split(None, 1) + if len(subject) > 60: + subject = subject[:58].rstrip() + '...' + commits += '!gravatar(%s) %s\n' % (email, subject) + return commits + +def send_bot_message(oldrev, newrev, refname): + repo_name = git_repository_name() + branch = refname.replace('refs/heads/', '') + destination = config.commit_notice_destination(repo_name, branch, newrev) + if destination is None: + # Don't forward the notice anywhere + return + + added = git_commit_range(oldrev, newrev) + removed = git_commit_range(newrev, oldrev) + + new_head = newrev[:12] + + if removed: + message = '`%s` was pushed to `%s`, **REMOVING**:\n\n%s' % (new_head, branch, removed) + if added: + message += '\n**and adding**:\n\n' + added + message += '\n**A HISTORY REWRITE HAS OCCURRED!**' + message += '\nPlease check your local branches to deal with this.' + elif added: + message = '`%s` was deployed to `%s` with:\n\n%s' % (new_head, branch, added) + else: + message = '`%s` was pushed to `%s`... but nothing changed?' % (new_head, branch) + + message_data = { + "type": "stream", + "to": destination["stream"], + "subject": destination["subject"], + "content": message, + } + client.send_message(message_data) + +for ln in sys.stdin: + oldrev, newrev, refname = ln.strip().split() + send_bot_message(oldrev, newrev, refname) diff --git a/bots/humbug_git_config.py b/bots/humbug_git_config.py new file mode 100644 index 0000000000..1ab0c6cdd8 --- /dev/null +++ b/bots/humbug_git_config.py @@ -0,0 +1,34 @@ +# Humbug Inc's internal git plugin configuration. +# The plugin and example config are under api/integrations/ + +# Leaving all the instructions out of this file to avoid having to +# sync them as we update the comments. + +HUMBUG_USER = "humbug+commits@humbughq.com" +HUMBUG_API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +# commit_notice_destination() lets you customize where commit notices +# are sent to. +# +# It takes the following arguments: +# * repo = the name of the git repository +# * branch = the name of the branch that was pushed to +# * commit = the commit id +# +# Returns a dictionary encoding the stream and subject to send the +# notification to (or None to send no notification, e.g. for ). +# +# The default code below will send every commit pushed to "master" to +# * stream "commits" +# * subject "deploy => master" (using a pretty unicode right arrow) +# And similarly for branch "test-post-receive" (for use when testing). +def commit_notice_destination(repo, branch, commit): + if branch in ["master", "post-receive-test"]: + return dict(stream = "commits", + subject = u"deploy \u21D2 %s" % (branch,)) + + # Return None for cases where you don't want a notice sent + return None + +HUMBUG_API_PATH = "/home/humbug/humbug/api" +HUMBUG_SITE = "https://staging.humbughq.com" diff --git a/templates/zephyr/integrations.html b/templates/zephyr/integrations.html index 7d260983ae..344dc3e62c 100644 --- a/templates/zephyr/integrations.html +++ b/templates/zephyr/integrations.html @@ -33,9 +33,7 @@

Services with integrations

+{#--------------------------------------------------------------------#} +
+

Git

+

First, download and install our Python + bindings and example scripts on your Git server.

+ +

Next, open integrations/git/humbug_git_config.py + in your favorite editor, and change the following lines to + specify the email address and API key for your Git bot:

+ + +
HUMBUG_USER = "git@example.com"
+HUMBUG_API_KEY = "0123456789abcdef0123456789abcdef"
+ +

You can also specify which pushes will result in + notifications and to what stream the notifications will be sent + by modifying the commit_notice_destination function + in humbug_git_config.py. By default, pushes to + the master and test-post-receive + branches will result in a notification to + stream commits.

+ +

Save integrations/git/humbug_git_config.py to + the .git/hooks directory of your git + repository.

+ +

Symlink + /usr/local/share/humbug/integrations/git/post-receive + into the .git/hooks directory of your git repository.

+ +

Congratulations! You're done!
Whenever you make + a push to the master branch of your git repository + (or whatever you configured above), the Humbug git plugin will + send an automated notification that looks like this:

+ + +

Testing
You can test the plugin without changing + your master branch by pushing to + the test-post-receive branch.

+ +

+ ^ Back to top +

+
+

GitHub

First, go to your repository page and click "Settings":

diff --git a/tools/post-receive b/tools/post-receive index 932b4b9dbf..5173c9d52d 100755 --- a/tools/post-receive +++ b/tools/post-receive @@ -22,14 +22,9 @@ import subprocess import time from os import path -sys.path.append(path.join(path.dirname(os.readlink(__file__)), '../api')) -import humbug -client = humbug.Client( - email="humbug+commits@humbughq.com", - site="https://staging.humbughq.com", - api_key="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - verbose=False) +import os.path +# check_output is backported from subprocess.py in Python 2.7 def check_output(*popenargs, **kwargs): if 'stdout' in kwargs: raise ValueError('stdout argument not allowed, it will be overridden.') @@ -49,50 +44,17 @@ def update_deployment(server, oldrev, newrev, refname): "/home/humbug/humbug/tools/update-deployment", oldrev, newrev, refname]) -def git_commit_range(oldrev, newrev): - log_cmd = ["git", "log", "--reverse", - "--pretty=%aE %s", "%s..%s" % (oldrev, newrev)] - commits = '' - for ln in subprocess.check_output(log_cmd).splitlines(): - email, subject = ln.split(None, 1) - # This truncation approximately fits the normal message width in the browser. - # It would be nicer to move this into the client via CSS and a Markdown extension. - if len(subject) > 60: - subject = subject[:58].rstrip() + '...' - commits += '!gravatar(%s) %s\n' % (email, subject) - return commits - -def send_bot_message(oldrev, newrev, refname): - added = git_commit_range(oldrev, newrev) - removed = git_commit_range(newrev, oldrev) - - new_head = newrev[:12] - branch = refname.replace('refs/heads/', '') - - if removed: - message = '`%s` was pushed to `%s`, **REMOVING**:\n\n%s' % (new_head, branch, removed) - if added: - message += '\n**and adding**:\n\n' + added - message += '\n**A HISTORY REWRITE HAS OCCURRED!**' - message += '\nPlease check your local branches to deal with this.' - elif added: - message = '`%s` was deployed to `%s` with:\n\n%s' % (new_head, branch, added) - else: - message = '`%s` was pushed to `%s`... but nothing changed?' % (new_head, branch) - - message_data = { - "type": "stream", - "to": "test" if refname == "refs/heads/test-post-receive" else "devel", - "subject": u"deploy \u21D2 " + branch, - "content": message, +deployments = { + 'refs/heads/prod': ('humbughq.com', 'prod'), + 'refs/heads/master': ('staging.humbughq.com', 'master'), + 'refs/heads/test-post-receive': ('staging.humbughq.com', 'master'), } - client.send_message(message_data) for ln in sys.stdin: oldrev, newrev, refname = ln.strip().split() - if refname in ["refs/heads/prod"]: - send_bot_message(oldrev, newrev, refname) - update_deployment("humbughq.com", oldrev, newrev, "origin/prod") - elif refname in ["refs/heads/master", "refs/heads/test-post-receive"]: - send_bot_message(oldrev, newrev, refname) - update_deployment("staging.humbughq.com", oldrev, newrev, "origin/master") + if refname in deployments: + (server, branch) = deployments[refname] + p = subprocess.Popen("/home/humbug/humbug/api/integrations/git/post-receive", + stdin=subprocess.PIPE) + p.communicate(input=ln) + update_deployment(server, oldrev, newrev, branch) diff --git a/zephyr/static/images/integrations/git/001.png b/zephyr/static/images/integrations/git/001.png new file mode 100644 index 0000000000..858e749e7e Binary files /dev/null and b/zephyr/static/images/integrations/git/001.png differ