#!/usr/bin/env bash
# Real demos for P069 "Golang Concurrency Will Finally Make Sense After This". Runs on render01 (Docker).
# One lab "laptop" (the official golang:1.27 image; hostname laptop, user ana, no network: the suppliers' stock
# service runs on the same machine at 127.0.0.1:8080 and answers each request after one second, like a slow
# supplier over the internet). Every command is typed into one real interactive bash (a pty); demo_output.json
# keeps every line, and build_script.py reads every number from it.
set -euo pipefail
cd "$(dirname "$0")"
P=ce-p069
cleanup() { docker rm -f $P-laptop >/dev/null 2>&1 || true; }
cleanup
trap cleanup EXIT
docker run -d --name $P-laptop --hostname laptop --network none golang:1.27 sleep infinity >/dev/null
docker exec $P-laptop useradd -m -s /bin/bash ana
docker exec $P-laptop mkdir -p /home/ana/shop
docker cp lab/. $P-laptop:/home/ana/shop/
docker exec $P-laptop chown -R ana:ana /home/ana
# off screen: build everything once (so `go run` doesn't compile on camera) and start the suppliers' service
docker exec -u ana -w /home/ana/shop -e HOME=/home/ana $P-laptop sh -c 'go build ./... && go build -race -o /dev/null ./race'
docker exec -d -u ana -w /home/ana/shop -e HOME=/home/ana $P-laptop go run ./suppliers
sleep 3

python3 - "$P" <<'PY'
import fcntl, json, os, pty, re, select, struct, subprocess, sys, termios, time
P = sys.argv[1]
READY = re.compile(rb"__READY__:(.*?):__ ")
steps = {}
class Shell:
    def __init__(self):
        pid, fd = pty.fork()
        if pid == 0:
            fcntl.ioctl(0, termios.TIOCSWINSZ, struct.pack("HHHH", 50, 100, 0, 0))
            os.execvp("docker", ["docker", "exec", "-it", "-u", "ana", "-w", "/home/ana/shop", "-e", "HOME=/home/ana",
                                 "-e", "TERM=dumb", f"{P}-laptop", "bash", "-i"])
        self.fd, self.where = fd, "~/shop"
        os.write(fd, b"stty -echo; stty cols 100 rows 50; PS1='__REA''DY__:\\w:__ '\n")
        self.read()
    def read(self, timeout=600):
        buf, end = b"", time.time() + timeout
        while time.time() < end:
            if select.select([self.fd], [], [], 0.3)[0]:
                buf += os.read(self.fd, 65536)
                m = None
                for m in READY.finditer(buf):
                    pass
                if m:
                    self.where = m.group(1).decode()
                    out = buf[:m.start()].replace(b"\r\r\n", b"\n").replace(b"\r\n", b"\n").decode()
                    return "\n".join(l.rstrip() for l in out.split("\n")).strip("\n")
        raise SystemExit(f"timed out; last output: {buf[-600:]!r}")
    def run(self, key, cmd):
        shown = f"ana@laptop:{self.where}$ {cmd}"
        os.write(self.fd, cmd.encode() + b"\n")
        out = self.read()
        steps[key] = {"cmd": shown, "out": out}
        print(f"--- {key}\n{shown}\n{out}\n", flush=True)
s = Shell()
s.run("h_seq", "go run ./sequential")
s.run("h_go", "go run ./gokeyword")
s.run("h_diff", "diff sequential/main.go gokeyword/main.go")
s.run("wg1", "go run ./waitgroup")
s.run("wg2", "go run ./waitgroup")
s.run("ch", "go run ./channels")
s.run("many", "go run ./many")
s.run("stack", "ulimit -s")
s.run("race1", "go run ./race")
s.run("race2", "go run ./race")
s.run("race3", "go run ./race")
s.run("racedet", "go run -race ./race")
s.run("mu_code", "sed -n '17,19p' mutex/main.go")
s.run("mutex", "go run ./mutex")
s.run("dl_cat", "cat deadlock/main.go")
s.run("dl", "go run ./deadlock")
s.run("timeout", "go run ./timeout")
s.run("version", "go version")
steps["_meta"] = {"cmd": "", "out": json.dumps({"run_at": time.strftime("%Y-%m-%d %H:%M %Z")})}
json.dump(steps, open("demo_output.json", "w"), indent=1)
print("saved demo_output.json")
PY
