Skip to content

Instantly share code, notes, and snippets.

@jtsagata
Last active August 29, 2015 14:08
Show Gist options
  • Select an option

  • Save jtsagata/b284622ed7c2af8ecf17 to your computer and use it in GitHub Desktop.

Select an option

Save jtsagata/b284622ed7c2af8ecf17 to your computer and use it in GitHub Desktop.
Poor mens CI. Continious Integration with Zeus, RSpec, tmux/tmuxinator like clone. Autotest and autoreload functionality (Work in progress)
#!/usr/bin/env python
# filename <app>/ci_tool
# Zeus CI IDE
# Created by talos aka Giannis Tsagatakis
# --color
# --require spec_helper
# --format documentation
# --format html -o "tmp/rspec.html"
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Vte, Gio, GLib, GObject, WebKit
import os, subprocess, pyinotify
GObject.threads_init()
site_url = "http://myhost.no-ip.org:3000/"
rails_root = os.getcwd()
# socket = rails_root+"/.zeus.sock"
# if os.path.exists(socket):
# os.remove(socket)
class MyTerm(Vte.Terminal):
def __init__(self, command, *args, **kwds):
super(MyTerm, self).__init__(*args, **kwds)
self.command = command
self.runcmd()
self.connect ("child-exited", lambda term: self.rerun())
self
def runcmd(self):
self.fork_command_full(
Vte.PtyFlags.DEFAULT,
rails_root,
self.command,
[],
GLib.SpawnFlags.DO_NOT_REAP_CHILD,
None,
None,
)
def rerun(self):
self.runcmd()
class ZeusWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Zues")
self.set_border_width(5)
self.set_default_size(400, 300)
self.console = MyTerm(["/bin/bash", "-c", "zeus start"])
self.add(self.console)
self.connect("delete-event", self.on_hide )
self.set_icon_name('abrt')
def on_hide(self,*args):
self.toogle(args)
def toogle(self,event):
if self.get_visible():
self.hide()
else:
self.show_all()
class Launcher(Gtk.Button):
def __init__(self, iconic, command, *args, **kwds):
super(Launcher, self).__init__(*args, **kwds)
icon = Gio.ThemedIcon(name=iconic)
image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
self.add(image)
self.connect("clicked", lambda x: subprocess.Popen(command) )
class RailsTab1(Gtk.VPaned):
def __init__(self, *args, **kwds):
super(RailsTab1, self).__init__(*args, **kwds)
self.console1 = MyTerm(["/bin/bash", "-c", "tail -f log/development.log"])
self.add(self.console1)
self.console2 = MyTerm(["/bin/bash", "-c", "zeus server"])
self.add(self.console2)
self.set_position(200)
class RailsTab2(Gtk.VPaned):
def __init__(self, *args, **kwds):
super(RailsTab2, self).__init__(*args, **kwds)
self.console1 = MyTerm(["/bin/bash", "-c", "bundle exec guard"])
self.add(self.console1)
self.console2 = MyTerm(["/bin/bash", "-c", "zeus console"])
self.add(self.console2)
self.set_position(200)
class RailsTab3(Gtk.VPaned):
def __init__(self, *args, **kwds):
super(RailsTab3, self).__init__(*args, **kwds)
self.console1 = MyTerm(["/bin/bash"])
self.add(self.console1)
self.console2 = MyTerm(["/bin/zsh"])
self.add(self.console2)
self.set_position(60)
class LivePreview(Gtk.VPaned):
def __init__(self, *args, **kwds):
super(LivePreview, self).__init__(*args, **kwds)
scroller = Gtk.ScrolledWindow()
web = WebKit.WebView()
scroller.add(web)
self.add(scroller)
web.open(site_url)
self.page = web
class RSpecView(Gtk.VPaned):
def __init__(self, *args, **kwds):
super(RSpecView, self).__init__(*args, **kwds)
scroller = Gtk.ScrolledWindow()
web = WebKit.WebView()
scroller.add(web)
self.add(scroller)
web.open("file://"+ rails_root + "/tmp/rspec.html")
self.page = web
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Rails CI IDE")
self.set_border_width(5)
self.set_default_size(800, 500)
# GTK Header
hb = Gtk.HeaderBar()
hb.set_show_close_button(True)
hb.props.title = "Rails CI IDE"
zeus_button = Gtk.Button()
icon = Gio.ThemedIcon(name="zim")
zeus_image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
zeus_button.add(zeus_image)
hb.pack_end(zeus_button)
self.zeus_button = zeus_button
hb.pack_end( Launcher("git-cola",["git-cola"]))
hb.pack_end( Launcher("gitg",["gitg"]))
hb.pack_end( Launcher("nemo",["nemo", rails_root]))
self.set_titlebar(hb)
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
Gtk.StyleContext.add_class(box.get_style_context(), "linked")
button = Gtk.Button()
button.add(Gtk.Arrow(Gtk.ArrowType.LEFT, Gtk.ShadowType.NONE))
button.connect("clicked", lambda w: self.prev_page())
box.add(button)
button = Gtk.Button()
button.add(Gtk.Arrow(Gtk.ArrowType.RIGHT, Gtk.ShadowType.NONE))
button.connect("clicked", lambda w: self.next_page())
box.add(button)
hb.pack_start(box)
vbox = Gtk.VBox(False, 0)
self.add(vbox)
# self.console = MyTerm(["/bin/bash", "-c", "zeus console"])
# vbox.add(self.console)
self.tab1 = RailsTab1()
label1 = Gtk.Label("Server")
self.tab2 = RailsTab2()
label2 = Gtk.Label("Console")
self.tab3 = RailsTab3()
label3 = Gtk.Label("Terminal")
self.web1 = LivePreview()
wlabel1 = Gtk.Label("Live: user")
self.web2 = RSpecView()
wlabel2 = Gtk.Label("CI View")
notebook = Gtk.Notebook()
notebook.set_tab_pos(Gtk.PositionType.BOTTOM)
notebook.append_page(self.tab1, label1)
notebook.append_page(self.tab3, label3)
notebook.prepend_page(self.tab2, label2)
notebook.append_page(self.web1, wlabel1)
notebook.append_page(self.web2, wlabel2)
vbox.add(notebook)
self.notebook = notebook
# Add status bar to vbox
# self.statusbar = Gtk.Statusbar()
# vbox.pack_start(self.statusbar, False, False, 0)
self.set_icon_name('abrt')
def next_page(self):
num_pages = self.notebook.get_n_pages()
cur_page = self.notebook.get_current_page()
if num_pages - 1 == cur_page:
self.notebook.set_current_page(0)
else:
self.notebook.next_page()
def prev_page(self):
cur_page = self.notebook.get_current_page()
num_pages = self.notebook.get_n_pages()
if cur_page == 0:
self.notebook.set_current_page( num_pages - 1 )
else:
self.notebook.prev_page()
zeus = ZeusWindow()
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.zeus_button.connect("clicked", zeus.toogle )
win.show_all()
wm = pyinotify.WatchManager() # Watch Manager
mask = pyinotify.IN_DELETE | pyinotify.IN_CREATE | pyinotify.IN_MODIFY
class ReloadHandler(pyinotify.ProcessEvent):
def process_IN_CREATE(self, event):
self.process(event.pathname)
def process_IN_DELETE(self, event):
self.process(event.pathname)
def process_IN_MODIFY(self, event):
self.process(event.pathname)
def process(self,pathname):
extension = os.path.splitext(pathname)[1]
win.web1.page.reload()
print "realoading: " + pathname
notifier1 = pyinotify.ThreadedNotifier(wm, ReloadHandler())
notifier1.start()
wdd = wm.add_watch('app', mask, rec=True)
# wdd = wm.add_watch('tmp/rspec.html', mask, rec=False)
def file_changed (monitor, file, unknown, event):
if event == Gio.FileMonitorEvent.CHANGES_DONE_HINT:
win.web2.page.reload()
print "realoading: specs"
rspec_status = rails_root + "/tmp/rspec.html"
monitor_file = Gio.File.new_for_path(rspec_status)
monitor = monitor_file.monitor_file(Gio.FileMonitorFlags.NONE, None)
monitor.connect ("changed", file_changed)
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment