Add support for external commands
If you run "gertty --open <URL>" it will instruct a running Gertty to open the change at that URL. Change-Id: Ie82aa53f497717e7355646d6d6fd12473ececad0
This commit is contained in:
parent
74877d2499
commit
1222a7f927
@ -47,6 +47,9 @@ servers:
|
||||
# time it starts (so that it does not grow without bound). If you
|
||||
# would like to log to a different location, you may specify it here.
|
||||
# log-file: ~/.gertty.log
|
||||
# Gertty listens on a unix domain socket for remote commands at
|
||||
# ~/.gertty.sock. You may change the path here:
|
||||
# socket: ~/.gertty.sock
|
||||
|
||||
# Gertty comes with two palettes defined internally. The default
|
||||
# palette is suitable for use on a terminal with a dark background.
|
||||
|
@ -19,6 +19,7 @@ import dateutil
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
@ -256,6 +257,8 @@ class App(object):
|
||||
self.error_queue = queue.Queue()
|
||||
self.error_pipe = self.loop.watch_pipe(self._errorPipeInput)
|
||||
self.logged_warnings = set()
|
||||
self.command_pipe = self.loop.watch_pipe(self._commandPipeInput)
|
||||
self.command_queue = queue.Queue()
|
||||
|
||||
warnings.showwarning = self._showWarning
|
||||
|
||||
@ -268,6 +271,9 @@ class App(object):
|
||||
|
||||
self.loop.screen.tty_signal_keys(start='undefined', stop='undefined')
|
||||
#self.loop.screen.set_terminal_properties(colors=88)
|
||||
|
||||
self.startSocketListener()
|
||||
|
||||
if not disable_sync:
|
||||
self.sync_thread = threading.Thread(target=self.sync.run, args=(self.sync_pipe,))
|
||||
self.sync_thread.daemon = True
|
||||
@ -294,6 +300,35 @@ class App(object):
|
||||
|
||||
self.popup(dialog)
|
||||
|
||||
def startSocketListener(self):
|
||||
if os.path.exists(self.config.socket_path):
|
||||
os.unlink(self.config.socket_path)
|
||||
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self.socket.bind(self.config.socket_path)
|
||||
self.socket.listen(1)
|
||||
self.socket_thread = threading.Thread(target=self._socketListener)
|
||||
self.socket_thread.daemon = True
|
||||
self.socket_thread.start()
|
||||
|
||||
def _socketListener(self):
|
||||
while True:
|
||||
try:
|
||||
s, addr = self.socket.accept()
|
||||
self.log.debug("Accepted socket connection %s" % (s,))
|
||||
buf = ''
|
||||
while True:
|
||||
buf += s.recv(1)
|
||||
if buf[-1] == '\n':
|
||||
break
|
||||
buf = buf.strip()
|
||||
self.log.debug("Received %s from socket" % (buf,))
|
||||
s.close()
|
||||
parts = buf.split()
|
||||
self.command_queue.put((parts[0], parts[1:]))
|
||||
os.write(self.command_pipe, six.b('command\n'))
|
||||
except Exception:
|
||||
self.log.exception("Exception in socket handler")
|
||||
|
||||
def clearInputBuffer(self):
|
||||
if self.input_buffer:
|
||||
self.input_buffer = []
|
||||
@ -598,7 +633,18 @@ class App(object):
|
||||
if category == requestsexceptions.InsecureRequestWarning:
|
||||
return
|
||||
self.error_queue.put(('Warning', m))
|
||||
os.write(self.error_pipe, 'error\n')
|
||||
os.write(self.error_pipe, six.b('error\n'))
|
||||
|
||||
def _commandPipeInput(self, data=None):
|
||||
(command, data) = self.command_queue.get()
|
||||
if command == 'open':
|
||||
url = data[0]
|
||||
self.log.debug("Opening URL %s" % (url,))
|
||||
result = self.parseInternalURL(url)
|
||||
if result is not None:
|
||||
self.openInternalURL(result)
|
||||
else:
|
||||
self.log.error("Unable to parse command %s with data %s" % (command, data))
|
||||
|
||||
def toggleHeldChange(self, change_key):
|
||||
with self.db.getSession() as session:
|
||||
@ -709,6 +755,21 @@ class PrintPaletteAction(argparse.Action):
|
||||
print(attr)
|
||||
sys.exit(0)
|
||||
|
||||
class OpenChangeAction(argparse.Action):
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
cf = config.Config(namespace.server, namespace.palette,
|
||||
namespace.keymap, namespace.path)
|
||||
url = values[0]
|
||||
result = urlparse.urlparse(values[0])
|
||||
if not url.startswith(cf.url):
|
||||
print('Supplied URL must start with %s' % (cf.url,))
|
||||
sys.exit(1)
|
||||
|
||||
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
s.connect(cf.socket_path)
|
||||
s.sendall('open %s\n' % url)
|
||||
sys.exit(0)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Console client for Gerrit Code Review.')
|
||||
@ -728,6 +789,9 @@ def main():
|
||||
help='print the keymap command names to stdout')
|
||||
parser.add_argument('--print-palette', nargs=0, action=PrintPaletteAction,
|
||||
help='print the palette attribute names to stdout')
|
||||
parser.add_argument('--open', nargs=1, action=OpenChangeAction,
|
||||
metavar='URL',
|
||||
help='open the given URL in a running Gertty')
|
||||
parser.add_argument('--version', dest='version', action='version',
|
||||
version=version(),
|
||||
help='show Gertty\'s version')
|
||||
|
@ -47,6 +47,7 @@ class ConfigSchema(object):
|
||||
'dburi': str,
|
||||
v.Required('git-root'): str,
|
||||
'log-file': str,
|
||||
'socket': str,
|
||||
'auth-type': str,
|
||||
}
|
||||
|
||||
@ -173,6 +174,8 @@ class Config(object):
|
||||
self.git_root = os.path.expanduser(server['git-root'])
|
||||
self.dburi = server.get('dburi',
|
||||
'sqlite:///' + os.path.expanduser('~/.gertty.db'))
|
||||
socket_path = server.get('socket', '~/.gertty.sock')
|
||||
self.socket_path = os.path.expanduser(socket_path)
|
||||
log_file = server.get('log-file', '~/.gertty.log')
|
||||
self.log_file = os.path.expanduser(log_file)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user