import socket import time from subprocess import check_call from genshi.builder import tag from trac.core import implements, Component from trac.ticket.api import ITicketActionController from trac.ticket.default_workflow import ConfigurableTicketWorkflow from trac.web.chrome import add_warning from BranchActor import BranchActor from RebranchActor import RebranchActor from MergeActor import MergeActor from CheckMergeActor import CheckMergeActor from action_logic import is_branchable, is_rebranchable, is_mergeable, is_checkmergeable class MergebotActionController(Component): """Support branching and merging operations for tickets. """ implements(ITicketActionController) # ITicketActionController def get_ticket_actions(self, req, ticket): controller = ConfigurableTicketWorkflow(self.env) mergebot_operations = self._get_available_operations(req, ticket) if mergebot_operations: actions_we_handle = controller.get_actions_by_operation_for_req(req, ticket, 'mergebot') else: actions_we_handle = [] return actions_we_handle def get_all_status(self): return [] def render_ticket_action_control(self, req, ticket, action): actions = ConfigurableTicketWorkflow(self.env).actions label = actions[action]['name'] hint = '' control_id = action + '_mergebot_op' mergebot_operations = self._get_available_operations(req, ticket) # TODO: allow the configuration to specify a sub-set of permitted # mergebot actions #self.config.get('ticket-workflow', action + '.mergebot') self.env.log.debug('render_ticket_action_control ops: %s', mergebot_operations) selected_value = req.args.get(control_id) or mergebot_operations[0] control = tag.select([tag.option(option, selected=(option == selected_value or None)) for option in mergebot_operations], name=control_id, id=control_id) return (label, control, hint) def get_ticket_changes(self, req, ticket, action): control_id = action + '_mergebot_op' selected_value = req.args.get(control_id) add_warning(req, 'Mergebot operation %s will be triggered' % selected_value) return {} def daemon_address(self): host = self.env.config.get('mergebot', 'listen.ip') port = self.env.config.getint('mergebot', 'listen.port') return (host, port) def start_daemon(self): check_call(['mergebotdaemon', self.env.path]) time.sleep(1) # bleh def _daemon_cmd(self, cmd): self.log.debug('Sending mergebotdaemon: %r' % cmd) try: info_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) info_socket.connect(self.daemon_address()) except socket.error, e: # if we're refused, try starting the daemon and re-try if e and e[0] == 111: self.log.debug('connection to mergebotdaemon refused, trying to start mergebotdaemon') self.start_daemon() self.log.debug('Resending mergebotdaemon: %r' % cmd) info_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) info_socket.connect(self.daemon_address()) info_socket.sendall(cmd) self.log.debug('Reading mergebotdaemon response') raw = info_socket.recv(4096) info_socket.close() self.log.debug('Reading mergebotdaemon response was %r' % raw) return raw def apply_action_side_effects(self, req, ticket, action): self.log.info('applying mergebot side effect for ticket %s on action %s' % (ticket.id, action)) control_id = action + '_mergebot_op' selected_value = req.args.get(control_id) result = self._daemon_cmd('ADD %s %s %s %s %s\n\QUIT\n' % (ticket.id, selected_value, ticket['component'], ticket['version'], req.authname or 'anonymous')) if 'OK' not in result: add_warning(req, result) # adding warnings in this method doesn't seem to work def _get_available_operations(self, req, ticket): mergebot_ops = [] if req.perm.has_permission("MERGEBOT_BRANCH"): if is_branchable(ticket): mergebot_ops.append('branch') if is_rebranchable(ticket): mergebot_ops.append('rebranch') if is_checkmergeable(ticket) and req.perm.has_permission('MERGEBOT_VIEW'): mergebot_ops.append('checkmerge') if ticket['version'].startswith("#"): may_merge = req.perm.has_permission("MERGEBOT_MERGE_TICKET") else: may_merge = req.perm.has_permission("MERGEBOT_MERGE_RELEASE") if may_merge and is_mergeable(ticket): mergebot_ops.append('merge') return mergebot_ops