# copyright 2009 Jason Penney # # This file is part of flashbake. # # flashbake is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # flashbake is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with flashbake. If not, see <http://www.gnu.org/licenses/>. # growl.py - Growl notification flashbake plugin from flashbake import plugins import flashbake import logging import os import re import subprocess class Growl(plugins.AbstractNotifyPlugin): def __init__(self, plugin_spec): plugins.AbstractPlugin.__init__(self, plugin_spec) self.define_property('host') self.define_property('port') self.define_property('password') self.define_property('growlnotify') self.define_property('title_prefix', default='fb:') def init(self, config): if self.growlnotify == None: self.growlnotify = flashbake.find_executable('growlnotify') if self.growlnotify == None: raise plugins.PluginError(plugins.PLUGIN_ERRORS.ignorable_error, #@UndefinedVariable self.plugin_spec, 'Could not find command, growlnotify.') # TODO: use netgrowl.py (or wait for GNTP support to be finalized # so it will support Growl for Windows as well) def growl_notify(self, title, message): args = [ self.growlnotify, '--name', 'flashbake' ] if self.host != None: args += [ '--udp', '--host', self.host] if self.port != None: args += [ '--port', self.port ] if self.password != None: args += [ '--password', self.password ] title = ' '.join([self.title_prefix, title]) args += ['--message', message, '--title', title] subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) def warn(self, hot_files, config): ''' Emits one message per file, with less explanation than the email plugin. The most common case is that one or two files will be off, a large number of them can be considered pathological, e.g. someone who didn't read the documentation about lack of support for symlinks, for instance. ''' # if calling growl locally, then the current user must be logged into the console if self.host == None and not self.__active_console(): logging.debug('Current user does not have console access.') return logging.debug('Trying to warn via growl.') project_name = os.path.basename(hot_files.project_dir) [self.growl_notify('Missing in project, %s' % project_name, 'The file, "%s", is missing.' % file) for file in hot_files.not_exists] [self.growl_notify('Deleted in project, %s' % project_name, 'The file, "%s", has been deleted from version control.' % file) for file in hot_files.deleted] [self.growl_notify('Link in project, %s' % project_name, 'The file, "%s", is a link.' % file) for (file, link) in hot_files.linked_files.iteritems() if file == link] [self.growl_notify('Link in project, %s' % project_name, 'The file, "%s", is a link to %s.' % (link, file)) for (file, link) in hot_files.linked_files.iteritems() if file != link] [self.growl_notify('External file in project, %s' % project_name, 'The file, "%s", exists outside of the project directory.' % file) for file in hot_files.outside_files] def notify_commit(self, to_commit, hot_files, config): logging.debug('Trying to notify via growl.') self.growl_notify(os.path.basename(hot_files.project_dir), 'Tracking changes to:\n' + '\n'.join(to_commit)) def __whoami(self): cmd = flashbake.find_executable('whoami') if cmd: proc = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) return proc.communicate()[0].strip() else: return None def __active_console(self): user = self.__whoami() if not user: return False cmd = flashbake.find_executable('who') if not cmd: return False proc = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) active = False for line in proc.communicate()[0].splitlines(): m = re.match('^%s\s+console.*$' % user, line) if m: active = True break return active