iTerm2 (Python API): Automatically starting multiple services in separate panes
There are awesome process monitors like PM2 to help manage multiple services, but sometimes you need to boot up multiple fully-dockerized workflows and place them in separate panes for easy log monitoring. iTerm2 has a great Python API to help automatize these repetitive tasks.
Unfortunately, most examples I found on the web using the iTerm2 Python API started everything in parallel. I needed to actually wait for each server to print its “ready/listening” message before moving on to the next one. This rules out port clash problems, synchronisation conflicts, etc. Also, when running on a laptop, sometimes there is not enough CPU power to boot up everything simultaneously.
Here is my example script for starting multiple services in iTerm2, each on their
separate pane. Each one will wait for a completion message before opening the next pane. I separated the logic into a neat open_pane_and_start_service
function that you can call multiple times for starting up all the services you need.
Where to put the script
To use it, save it with a .py
extension in the iTerm2 scripts folder.
Opening the iTerm2 Python Scripts folder
The script will then appear in the list of available scripts:
Script visible in the list of iTerm scripts
What it does, in detail
The script will:
- Open a new pane at a folder that you specify
- Play with
Vertical=True
orVertical=False
to achieve the layout you need - Provide an optional
pane_to_split
value to specify a previous pane to split from
- Play with
- Run a command
- Wait for a certain line to appear on the screen (typically
Listening at...
or similar) - Move on to the next service.
Personally, I use this with multiple mutagen sessions, which sometimes can take 10+ minutes to perform a full file synchronization first thing in the morning.
The code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!/usr/bin/env python3.7
import asyncio
import re
import iterm2
async def open_pane_and_start_service(
window,
fs_path,
command,
string_to_detect_to_finish,
pane_to_split=None,
vertical=False,
):
tab = window.current_tab
session = tab.current_session
pane = session
if pane_to_split is not None:
pane = await session.async_split_pane(vertical)
await pane.async_activate()
async def run_fs():
await pane.async_send_text('cd ' + fs_path + '\n')
await pane.async_send_text(command + '\n')
finished = False
async with pane.get_screen_streamer() as streamer:
while not finished:
stringified_string_contents = ""
screen_output = await streamer.async_get()
for line_no in range(screen_output.number_of_lines + screen_output.number_of_lines_above_screen):
stringified_string_contents = stringified_string_contents + screen_output.line(line_no).string
if len(stringified_string_contents) > 0:
finished = re.search(pattern=string_to_detect_to_finish,
string=stringified_string_contents,
flags=re.IGNORECASE) is not None
print("Found string [ " + string_to_detect_to_finish + " ] , service is started.")
return pane
loop = asyncio.get_event_loop()
f_task = loop.create_task(run_fs())
await f_task
return pane
async def main(connection):
app = await iterm2.async_get_app(connection)
window = app.current_terminal_window
if window is not None:
# Service 1
service_1 = await open_pane_and_start_service(
window,
"~/GitHub/service_1",
"make start-dev",
"Watching for changes...",
vertical=False,
)
# Service 2
service_2 = await open_pane_and_start_service(
window,
"~/GitHub/service_2",
"make start-dev",
"compiled successfully in",
pane_to_split=service_1,
vertical=True
)
# Service 3
service_2 = await open_pane_and_start_service(
window,
"~/GitHub/service_3",
"make build && make start",
"Listening on",
vertical=False,
pane_to_split = service_2
)
# Service 4
service_4 = await open_pane_and_start_service(
window,
"~/GitHub/service_4",
"make start",
"Server started at",
vertical=True,
pane_to_split = service_3
)
else:
print("No current window")
iterm2.run_until_complete(main)
Now you can start all your microservices while you brew your morning coffee!
Comments
Post comment