From 4caafd66ce4a7a1ecc7be0fa52b80989bc8fad08 Mon Sep 17 00:00:00 2001 From: Tim Abbott Date: Fri, 15 Feb 2013 17:01:55 -0500 Subject: [PATCH] Rewrite git hook for distribution and document it. (imported from commit c3238bc5b5e29727cddb43bc55ab418326226acd) --- api/integrations/git/humbug_git_config.py | 57 +++++++++ api/integrations/git/post-receive | 117 ++++++++++++++++++ bots/humbug_git_config.py | 34 +++++ templates/zephyr/integrations.html | 47 ++++++- tools/post-receive | 62 ++-------- zephyr/static/images/integrations/git/001.png | Bin 0 -> 21287 bytes 6 files changed, 265 insertions(+), 52 deletions(-) create mode 100644 api/integrations/git/humbug_git_config.py create mode 100755 api/integrations/git/post-receive create mode 100644 bots/humbug_git_config.py create mode 100644 zephyr/static/images/integrations/git/001.png 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 0000000000000000000000000000000000000000..858e749e7e8f2ef3245e7aa1c35bcdd117bb9cdb GIT binary patch literal 21287 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

QPeCm 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