Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:smarty12:Python
irker
irker-svnpoller
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File irker-svnpoller of Package irker
#!/usr/bin/env python """ irker-svnpoller - Polls a specified SVN commit and sends it to an IRC channel using irkerd Copyright (C) 2012 - 2013 by Ignacio Riquelme Morelle <shadowm2006@gmail.com> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice is present in all copies. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ import argparse, json, os, pysvn, re, socket, sys # Whether to send full commit messages, or just the subject # lines. COMMIT_SUBJECTS_ONLY = False # Maximum number of non-empty commit message body lines to # send to irkerd when using full commit messages. This value # is only used when COMMIT_SUBJECTS_ONLY is set to False. # If there are more non-empty lines than this value, "(Log # message trimmed)" will be added in a new line at the end. # When set to 0, it behaves as if COMMIT_SUBJECTS_ONLY were # set to True. MAX_COMMIT_BODY_LINES = 5 # Hostname to use when connecting to an irkerd instance. IRKER_HOST = 'localhost' # Port number to use when connecting to an irkerd instance. IRKER_PORT = 6659 # The commit notification template. You probably do not # need to change this. TEMPLATE = "%(bold)s%(project)s:%(bold)s %(green)s%(author)s%(reset)s * r%(bold)s%(revision)s%(bold)s /%(files)s%(bold)s:%(bold)s %(log)s" # The template used for continuation lines for full commit # messages. TEMPLATE_BODY = "%(bold)s%(project)s:%(bold)s %(line)s" # The template used for the "(Log message trimmed)" line. TEMPLATE_TRIMMED = "%(bold)s%(project)s:%(bold)s %(gray)s(Log message trimmed.)" # No user-serviceable parts below this line. debug = False simulate = False rangemode = False IRC_FORMAT_CONSTANTS = { 'bold': '\x02', 'green': '\x033', 'blue': '\x032', 'yellow': '\x037', 'gray': '\x0314', 'reset': '\x0F', } class IrkerSVNSource: "Represents a SVN repository from which irker notifications are generated." def __init__(self, project, repository): self.project = project self.repository = repository self._client = pysvn.Client() self._template = TEMPLATE self._body_template = TEMPLATE_BODY self._trim_template = TEMPLATE_TRIMMED def list_revisions_since(self, revision): "Return a list of all revisions on the remote repository newer than " "the one specified, or an empty list if there are no such." if type(revision) != type(int()): revision = int(revision) log = self._client.log( self.repository, pysvn.Revision(pysvn.opt_revision_kind.head), pysvn.Revision(pysvn.opt_revision_kind.head), True)[0] if not log: sys.stderr.write("Could not fetch HEAD revision info for %s\n" % self.repository) return [] head = log['revision'].number if head <= revision: sys.stderr.write("Revision %d is greater than HEAD (%d)\n" % revision, head) return [] return range(revision + 1, head + 1) def generate_notification(self, revision): "Generate a message for irkerd deliver from the given commit." if type(revision) != type(int()) and revision.upper() == 'HEAD': revobj = pysvn.Revision(pysvn.opt_revision_kind.head) else: revobj = pysvn.Revision(pysvn.opt_revision_kind.number, revision) log = self._client.log(self.repository, revobj, revobj, True)[0] if 'author' in log: author = log['author'] else: author = '<Unknown>' if 'message' in log: if COMMIT_SUBJECTS_ONLY: message = log['message'].splitlines()[0] else: message_lines = [] format_vars = IRC_FORMAT_CONSTANTS format_vars['project'] = self.project line_count = 0 for line in log['message'].splitlines(): # Skip empty lines. if line.strip() == '': continue line_count = line_count + 1 # Always let the subject line through. if line_count > MAX_COMMIT_BODY_LINES + 1: message_lines.append(self._trim_template % format_vars) break format_vars['line'] = line message_lines.append(self._body_template % format_vars) # Force a newline at the start when showing full commits, # so the stat summary and the subject are on different lines. message = "\n" + "\n".join(message_lines) else: message = '<Missing commit message>' format_vars = IRC_FORMAT_CONSTANTS format_vars.update({ 'project': self.project, 'branch': '', # unused for SVN 'module': '', # unused for SVN 'revision': log['revision'].number, 'author': author, 'log': message, 'files': self._format_paths(log['changed_paths']) }) msg = self._template % format_vars return msg def _format_paths(self, paths_dict): "Format a commit's changed paths list for display in a similar " "fashion to that of CIA bots." prefix, relative_paths = self._consolidate_paths(paths_dict) filelist_str = ' '.join(relative_paths) if len(filelist_str) > 60: filelist_str = self._format_path_summary(relative_paths) if prefix.startswith('/'): prefix = prefix[1:] if filelist_str: return "%s (%s)" % (prefix, filelist_str) return prefix def _consolidate_paths(self, paths_dict): "Return the common prefix for a commit's changed paths and the " "relative paths for each affected file." paths = [] for entry in paths_dict: paths.append(entry['path']) # Optimization: if we only have one file, don't waste CPU on any of the other # stuff we do to pretend to be CIA. if len(paths) == 1: return paths[0], [] prefix = re.sub("[^/]*$", "", os.path.commonprefix(paths)) relative_paths = [] for path in paths: relative = path[len(prefix):].strip() if relative == '': relative = '.' relative_paths.append(relative) return prefix, relative_paths def _format_path_summary(self, relative_paths): "Return a changed paths count summary for display." dirs = {} for path in relative_paths: dirs[os.path.split(path)[0]] = True if len(dirs) <= 1: return "%d files" % len(relative_paths) return "%d files in %d dirs" % (len(relative_paths), len(dirs)) def deliver_to_irker(uris, message): "Delivers the given notification for one or more channels to irkerd." envelope = json.dumps({ "to": uris, "privmsg": message }) if debug: print envelope if not simulate: (host, port) = (IRKER_HOST, IRKER_PORT) try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(envelope + "\n", (host, port)) finally: sock.close() if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-d', action='store_true', dest='debug', help="show extra debugging information") parser.add_argument('-n', action='store_true', dest='simulate', help="simulate, don't send anything to irker") parser.add_argument('-r', action='store_true', dest='rangemode', help="deliver notifications for all revisions newer than the specified revision up to HEAD") parser.add_argument('project_id') parser.add_argument('repository_URI') parser.add_argument('revision_or_HEAD') parser.add_argument('channel_URI', nargs='*') args = parser.parse_args() debug = args.debug simulate = args.simulate rangemode = args.rangemode source = IrkerSVNSource(args.project_id, args.repository_URI) try: rev_range = [] if rangemode: rev_range = source.list_revisions_since(args.revision_or_HEAD) else: rev_range = [ args.revision_or_HEAD ] for rev in rev_range: message = source.generate_notification(rev) deliver_to_irker(args.channel_URI, message) except pysvn.ClientError, e: sys.stderr.write("pysvn.ClientError: %s\n" % e.message) # kate: indent-mode normal; encoding utf-8; space-indent on;
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor