tmux-dev (8707B)
1 #!/bin/lua 2 3 ----------------------------- 4 ------ CONFIGURATION ------- 5 ----------------------------- 6 local session = 'medoro' -- name for tmux session 7 local workdir = '~/dev/medoro' -- your root dir for projects 8 local pub = '~/.dswitch/channels/stable/versions/2.10.4/dart-sdk/bin/pub' 9 10 local cmd = { 11 ui = string.format( 12 '%s get && %s run build_runner serve --delete-conflicting-outputs web:8081 >&1 > >(trun angulardart ui)', pub, pub), 13 dw = string.format( 14 '%s get && %s run build_runner serve --delete-conflicting-outputs web:8082 >&1 > >(trun angulardart dw)', pub, pub), 15 digi = string.format( 16 '%s get && %s run build_runner serve --delete-conflicting-outputs web:8083 >&1 > >(trun angulardart digi)', pub, pub), 17 dpgw = 'mvn compile && mvn exec:java -Dexec.mainClass=org.medoro.dpgw.DPGWMain', 18 r_dpgw = 'mvn exec:java -Dexec.mainClass=org.medoro.dpgw.DPGWMain', 19 } 20 ------ APP DEFINITION ------- 21 -- cwd: working directory 22 -- cmd: command to start/restart 23 local dpgw = {key = 'dpgw', cwd = workdir .. '/dpgw', cmd = cmd.dpgw, r_cmd = cmd.r_dpgw} 24 local ui = {key = 'ui', cwd = workdir .. '/ui/branches/devel', cmd = cmd.ui} 25 local dw = {key = 'dw', cwd = workdir .. '/dw/branches/devel', cmd = cmd.dw} 26 local digi = {key = 'digi', cwd = workdir .. '/digi/branches/devel', cmd = cmd.digi} 27 -- COUPLES 28 -- apps: list of app to make action on 29 local stack = {key = 'stack', apps = {dpgw, ui, dw}} 30 ------------------------------ 31 32 ------- ENABLED APPS ------- 33 local enabled_apps = {ui, dw, dpgw, digi, stack} 34 ----------------------------- 35 36 ------- USAGE ------- 37 local usage = [[ 38 usage: mdev <action> [<app>] 39 40 action 41 s <app> -- [s]tart | Start app in tmux session 42 r <app> -- [r]estart | Cancel command and rerun app cmd 43 t [<app>] -- [t]erm | Stop app or whole session if app not provided 44 o [<app>] -- [o]pen | Open tmux, if <app> is provided open app pane 45 (Only first app is taken) 46 j <app>... -- [j]oin | Join multiple app to single tmux window 47 48 start 49 app separated by space will start each one in new tmux window 50 > "app1 app2 app3" 51 52 to start multple apps in one tmux window, connect them with '+' 53 > "app1+app2 app3" 54 ]] 55 ----------------------------- 56 57 -- provide at least one argument or catch help for usage 58 if #arg == 0 or arg[1] == '-h' or arg[1] == '--help' then 59 io.write(usage, '\n') 60 os.exit(0) 61 end 62 63 local actions = {start = 's', restart = 'r', stop = 't', open = 'o', join = 'j'} -- command mapping 64 local action_set = {['s'] = true, ['r'] = true, ['t'] = true, ['o'] = true, ['j'] = true} 65 local action = string.sub(table.remove(arg, 1), 1, 1); -- get first char of command 66 -- check for valid command 67 if not action_set[action] then 68 io.write(usage, '\n') 69 os.exit(1) 70 end 71 72 -- make action 73 if #arg == 0 and action == actions.stop then 74 local _, _, code = os.execute(string.format('tmux kill-session -t %s', session)) 75 if code < 1 then io.write('Stopped session', '\n') end 76 os.exit(0) 77 elseif #arg == 0 then 78 os.exit(0) 79 end 80 81 ----------------------------- 82 ------ PROCESS UTILS ------- 83 ----------------------------- 84 local function started() 85 local _, _, code = os.execute(string.format('tmux has -t %s 2>> /dev/null', session)) 86 return code < 1 87 end 88 local function run(cmd, ...) return os.execute(string.format(cmd, ...)) end 89 90 -------------------------- 91 ------ TMUX UTILS ------- 92 -------------------------- 93 local function tlocation(pane) 94 local list = io.popen(string.format( 95 'tmux lsp -s -t %s -F "#{pane_title} #{window_index} #{pane_index}" 2>> /dev/null', session)) 96 local location 97 for line in list:lines() do 98 local p, wi, pi = string.match(line, '^(%a+) (%d+) (%d+)$') 99 if p == pane then location = string.format('%s:%s.%s', session, wi, pi) end 100 end 101 return location 102 end 103 local function tattach(pane) 104 local location = tlocation(pane) 105 if location then run('tmux a -t %s', location) end 106 end 107 local function tkillp(pane) 108 local location = tlocation(pane) 109 if location then run('tmux killp -t %s', location) end 110 end 111 local function tnew(key, cwd) 112 run('tmux neww -c %s -t %s -n %s', cwd, session, key) 113 run('tmux selectp -t %s:%s.0 -T %s', session, key, key) 114 end 115 local function tsend(pane, cmds) 116 local location = tlocation(pane) 117 if location then 118 if type(cmds) == 'table' then 119 for _, c in ipairs(cmds) do run('tmux send -t %s "%s" enter', location, c) end 120 else 121 run('tmux send -t %s "%s" enter', location, cmds) 122 end 123 return true 124 end 125 end 126 local function tjoin(target, source) 127 local spane = tlocation(source) 128 local tpane = tlocation(target) 129 run('tmux joinp -s %s -t %s', spane, tpane) 130 end 131 132 ------------------ 133 ------ APP ------- 134 ------------------ 135 local App = {} 136 137 ------ METATABLE ------- 138 function App:new(app) 139 self.__index = self 140 setmetatable(app, self) 141 app.run = {} 142 return app 143 end 144 ------------------------ 145 146 function App:start() 147 tnew(self.key, self.cwd) 148 tsend(self.key, self.cmd) 149 io.write(string.format('Started %s', self.key), '\n') 150 end 151 152 function App:restart() 153 tsend(self.key, {'^c', self.r_cmd or self.cmd}) 154 io.write(string.format('Restarted %s', self.key), '\n') 155 end 156 157 function App:stop() 158 tkillp(self.key) 159 io.write(string.format('Stopped %s', self.key), '\n') 160 -- TODO: check if window is not empty and close it.. maybe 161 end 162 163 function App:open() 164 local location = tlocation(self.key) 165 if location then tattach(self.key) end 166 end 167 168 --- if pane exists 169 function App:started() 170 return tlocation(self.key) 171 -- TODO: maybe check if command is running, is that possible? 172 end 173 174 ------ CREATE APPS ------- 175 local apps = {} 176 for _, app in ipairs(enabled_apps) do apps[app.key] = App:new(app) end 177 -------------------------- 178 179 ------ SESSION STARTED CHECK ------- 180 local fwin = 'initialize' -- name of first pane created alongside session 181 -- on START action - if session is not started, create new 182 if action == actions.start and not started() then 183 local ok = run('tmux new -s %s -n %s -d', session, fwin) 184 if not ok then 185 io.stderr.write('Could not run session.') 186 os.exit(1) 187 end 188 io.write(string.format('Started session "%s"', session), '\n') 189 end 190 -- on NON-START action - if session is not started, exit 191 if action ~= actions.start and not started() then 192 io.stderr:write('Session not running! Start session first.\n') 193 os.exit(1) 194 end 195 ------------------------------------ 196 197 ------ PARSE ARGS ------- 198 local curr_apps = {} 199 for _, a in ipairs(arg) do 200 local key_sequence = {} 201 for k, _ in string.gmatch(a, '(%a+)%+?') do 202 local app = apps[k] 203 if app then 204 -- COUPLES CHECK 205 -- expand couples 206 if app.apps then 207 for _, couple_apps in ipairs(app.apps) do table.insert(curr_apps, couple_apps) end 208 else 209 table.insert(key_sequence, app) 210 end 211 else 212 io.stderr:write('App with key "', k, '" not exists, ignoring.', '\n') 213 end 214 end 215 table.insert(curr_apps, key_sequence) 216 end 217 218 ------ MAIN LOGIC ------- 219 for _, capp in ipairs(curr_apps) do 220 local slaves = {} -- apps to join 221 if not capp.key then 222 for _, ca in ipairs(capp) do table.insert(slaves, ca) end 223 else 224 table.insert(slaves, capp) 225 end 226 227 for _, app in ipairs(slaves) do 228 if action == actions.start then 229 if app:started() then 230 io.stderr:write(string.format('App \'%s\' is already running.\n', app.key)) 231 else 232 app:start() 233 end 234 235 elseif action == actions.restart then 236 if not app:started() then 237 io.stderr:write(string.format('App \'%s\' not running.\n', app.key)) 238 else 239 app:restart() 240 end 241 242 elseif action == actions.stop then 243 if not app:started() then 244 io.stderr:write(string.format('App \'%s\' not running.\n', app.key)) 245 else 246 app:stop() 247 end 248 249 elseif action == actions.open then 250 if not app:started() then 251 io.stderr:write(string.format('App \'%s\' not running.\n', app.key)) 252 else 253 app:open() 254 end 255 end 256 end 257 258 -- join slaves to master 259 if action == actions.start or action == actions.join then 260 local master = table.remove(slaves, 1) 261 for _, app in ipairs(slaves) do tjoin(master.key, app.key) end 262 end 263 end 264 ------------------------ 265 266 ---------------------- 267 ------ CLEANUP ------- 268 ---------------------- 269 local anything_running = false 270 for _, app in pairs(apps) do 271 if app:started() then 272 anything_running = true 273 break 274 end 275 end 276 if anything_running then 277 -- kill initial pane when tmux is created 278 run('tmux killw -t %s:%s 2>> /dev/null', session, fwin) 279 else 280 -- kill whole session if nothing is running 281 run('tmux kill-session -t %s 2>> /dev/null', session) 282 io.write(string.format('killing session "%s"', session), '\n') 283 end