Skip to content

Instantly share code, notes, and snippets.

@ondreian
Last active May 29, 2020 14:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ondreian/ab31ca9738ac0bb382d785c8397a06c6 to your computer and use it in GitHub Desktop.
Save ondreian/ab31ca9738ac0bb382d785c8397a06c6 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
$0 = "lich"
##
## minimal options parser
##
module Opts
require "ostruct"
FLAG_PREFIX = "--"
def self.parse_command(h, c)
h[c.to_sym] = true
end
def self.parse_flag(h, f)
(name, val) = f[2..-1].split("=")
if val.nil?
h[name.to_sym] = true
else
val = val.split(",")
h[name.to_sym] = val.size == 1 ? val.first : val
end
end
def self.parse(args = ARGV)
config = OpenStruct.new
if args.size > 0
config = OpenStruct.new(**args.reduce(Hash.new) do |h, v|
if v.start_with?(FLAG_PREFIX)
parse_flag(h, v)
else
parse_command(h, v)
end
h
end)
end
config
end
@@cached = nil
def self.cached
@@cached ||= self.parse
end
def self.method_missing(method, *args)
cached.send(method, *args)
end
end
## minimal pretty logger
module Log
def self.out(msg, label: %i[], level: 0)
label = [label] unless label.is_a?(Array)
if msg.is_a?(Array) and level.eql?(0)
return msg.each {|msg| out(msg, label: label.slice(0..-1), level: level+1)}
end
puts %{[#{label.unshift($0).map(&:to_s).join(".")}] >> #{msg}}
end
end
# api for dealing with lich
module Lich
# you should edit this if it's different on your box
LICH_BIN = %(~/gemstone/lich/lich.rb)
LICH_PORTS = (8000..8999)
def self.sessions()
%x(ps a).split("\n")
.grep(%r[\-\-detachable-client=(?<port>\d+)])
.map {|session| session.split.slice(5..-1)}
end
def self.used_ports()
sessions.map {|sess| sess[3].split("=").last.to_i }
end
def self.next_open_port()
return LICH_PORTS.first if used_ports.empty?
(LICH_PORTS.to_a - used_ports).first
end
def self.cmd(name:, port:)
fail "#{port} is invalid" unless LICH_PORTS.include?(port)
%(ruby #{LICH_BIN} --login #{name} --detachable-client=#{port} --without-frontend 2> /dev/null &)
end
def self.login(name)
command = Lich.cmd(name: name, port: next_open_port)
Log.out(command, label: :login)
subprocess = fork {exec command}
Process.detach(subprocess)
end
def self.illthorn()
return unless %x(ps a).split("\n").grep("illthorn").empty?
Log.out("launching Illthorn FE", label: :illthorn)
subprocess = fork {exec %(illthorn)}
Process.detach(subprocess)
end
end
if Opts.help
print <<~HELP
\e[33mlich\e[0m
description: this util launches lich sessions in headless mode within the port range of 8000..8999
arguments:
--login=<string> a comma delimited list of characters to login
ex:
--login=Ondreian # logs in only Ondreian
--login=Ondreian,Etanamir # logs both Ondreian and Etanamir
HELP
exit
end
# do the work
return Log.out(Lich.sessions, label: %i(alive)) if Opts.list
fail "--login= is required" unless Opts.login
(Opts.login.is_a?(Array) ? Opts.login : [Opts.login])
.each {|name| Lich.login(name) }
# launch illthorn if desired
Lich.illthorn() if Opts.illthorn
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment