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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
#!/usr/bin/env python3
"""scripting -- built to be driven.
Left pane: a script's-eye view of the control socket -- the commands are
typed, the replies printed are byte-for-byte what the socket answers, and
the session performs the verb as each line lands. Right pane: a program
(deploy.py) that drew status text and real buttons into its own frame with
one escape sequence; the pointer clicks [Approve] and the click arrives
back on the program's stdin, which is where it wanted it.
"""
import os
import shutil
import sys
import tempfile
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from castgen import ROOT, Recorder, out_path
ENV = {"PS1": "$ ", "ENV": "/dev/null"}
# Amber, not the house pink: each cast in the tour records under its own
# theme, so scrolling to the next step reads as a scene change.
THEME = os.path.join(ROOT, "contrib", "themes", "amber.kdl")
work = tempfile.mkdtemp(prefix="slosh-demo-")
# The left pane's shell: reads a line, answers like the socket does. The
# replies below are copied from the real api replies at record time.
with open(os.path.join(work, "sh-replay"), "w") as f:
f.write(
"printf '$ '\n"
"while IFS= read -r l; do\n"
' case "$l" in\n'
' *\'"split"\'*) echo \'{"ok":true,"id":4}\' ;;\n'
" *set-name*) echo '{\"ok\":true}' ;;\n"
" *) : ;;\n"
" esac\n"
" printf '$ '\n"
"done\n"
)
# The right pane's program: asks for approval in its own frame.
with open(os.path.join(work, "deploy.py"), "w") as f:
f.write(
"import os, sys, tty\n"
"w = sys.stdout.write\n"
"w('\\033]5577;1;status;release 1.4.2 -- awaiting approval\\033\\\\')\n"
"w('\\033]5577;1;buttons;approve:Approve;cancel:Cancel\\033\\\\')\n"
"print('deploy: waiting for a human (or an agent)')\n"
"sys.stdout.flush()\n"
"tty.setcbreak(0)\n"
"buf = ''\n"
"while 'approve' not in buf:\n"
" buf += os.read(0, 64).decode('utf-8', 'replace')\n"
"w('\\033]5577;1;status;approved\\033\\\\')\n"
"print('deploy: approved -- rolling out')\n"
"sys.stdout.flush()\n"
"os.read(0, 64)\n"
)
lay = os.path.join(work, "demo.layout")
with open(lay, "w") as f:
f.write(
'layout {\n tab {\n pane focus=true cwd="%s" command="sh sh-replay"\n'
' pane cwd="%s" command="python3 deploy.py"\n }\n}\n' % (work, work)
)
r = Recorder(
["/bin/sh"],
cols=100,
rows=28,
title="built to be driven",
config=THEME,
env=ENV,
layout=lay,
)
r.capture(force=True, settle_ms=150)
r.pause(1.2)
# One JSON object per line: split, from outside.
r.type('slosh cmd \'{"cmd":"split","dir":"rows"}\'')
r.enter(dt=0.3)
reply = r.api("split", dir="rows")
r.capture(dt=0.4, settle_ms=80)
r.pause(1.4)
# Name the new pane from outside, too: the title changes where it stands.
new_id = reply["id"]
r.type(
'slosh cmd \'{"cmd":"set-name","target":"pane","id":%d,"name":"build"}\'' % new_id
)
r.enter(dt=0.3)
r.api("set-name", target="pane", id=new_id, name="build")
r.capture(dt=0.4, settle_ms=80)
r.pause(1.4)
# The program in the other pane asked its question in its own frame.
# Answer it with the pointer; the click lands on its stdin.
snap = r.snapshot()
hit = next(e for e in snap.hits if "approve" in e["action"])
r.move_to(hit["x"] + hit["w"] // 2, hit["y"], dur=0.9)
r.pause(0.6)
r.press()
r.release()
r.capture(dt=0.4, settle_ms=150)
r.pause(2.2)
r.save(out_path("scripting"), tail=2.0)
shutil.rmtree(work)
|