Initial build: scrawl, a shared ASCII canvas over SSH
ssh in and draw on a live, shared doodle wall -- everyone connected sees everyone else's edits in real time. Same wish+bubbletea security model as delve-term (no shell, no exec, structurally can't reach a real shell on the host), extended with the one thing delve-term didn't need: multiple sessions sharing live state. wish's bm.Middleware helper hides the *tea.Program it creates, so this builds the program directly instead, keeping a registry (game.Canvas) that broadcasts a redraw signal to every other connected session the instant one of them paints. Banner generated via `figlet -f slant SCRAWL` rather than hand-drawn -- slant felt right for a doodle/scribble tool. Two real bugs caught by actually running this with real SSH sessions, not just unit-testing the game logic in isolation: 1. Program.Send() blocks until that program's Run() event loop is actively reading from it. Broadcasting synchronously from inside Join()/Paint() (including a session broadcasting to its own, not-yet-running program on join) deadlocked every session before it ever reached Run() -- the very first connection just hung with nothing rendered. Fixed by sending asynchronously (go p.Send(...)) everywhere the canvas notifies sessions of a change. 2. Subtler: lipgloss's default package-level styles detect color support from the *server process's* os.Stdout, not any given session's actual terminal -- and a server's stdout is typically redirected (a log file, systemd journal), so every connected session silently lost all color/background styling at once, server-wide. Manifested as painting a cell your own cursor already sat on being invisible (the cursor glyph before/after looked identical, so bubbletea's diffing renderer correctly sent zero bytes for a change that produced no visual diff) -- confirmed via server-side debug logging that painting itself worked correctly every time, isolating the bug to rendering, then confirmed via a Go test that forcing a real color profile was the difference between 0 and 1327 runes of diff between two frames that should look different. Fixed with a lipgloss.Renderer created per-session, bound to that session's actual output, forced to TrueColor; cursors now highlight whatever's already painted at that cell (background tint) rather than replacing the character, so a session's own paint is never masked by its own cursor marker sitting on top of it. Verified end-to-end with two real, simultaneous SSH sessions (scripted via pexpect): peer count syncs correctly, and one session's paint genuinely arrives at the other via the live broadcast -- not just unit-tested in isolation. Also verified the actual Docker image builds and serves correctly over real SSH. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
53
.gitea/workflows/docker.yml
Normal file
53
.gitea/workflows/docker.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Derive image name and tags
|
||||
id: meta
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
SERVER_URL="${GITHUB_SERVER_URL:-${GITEA_SERVER_URL:-}}"
|
||||
REPO="${GITHUB_REPOSITORY:-${GITEA_REPOSITORY:-}}"
|
||||
SHA="${GITHUB_SHA:-${GITEA_SHA:-}}"
|
||||
if [[ -z "${SERVER_URL}" || -z "${REPO}" || -z "${SHA}" ]]; then
|
||||
echo "Missing SERVER_URL/REPO/SHA env vars." >&2; exit 1
|
||||
fi
|
||||
HOST=$(echo "${SERVER_URL}" | sed 's|https://||;s|http://||')
|
||||
IMAGE=$(echo "${HOST}/${REPO}" | tr '[:upper:]' '[:lower:]')
|
||||
SHORT_SHA=$(echo "${SHA}" | cut -c1-7)
|
||||
echo "host=${HOST}" >> "$GITHUB_OUTPUT"
|
||||
echo "image=${IMAGE}" >> "$GITHUB_OUTPUT"
|
||||
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Log in to Gitea container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ steps.meta.outputs.host }}
|
||||
username: ${{ github.actor }}
|
||||
# Create a Gitea PAT with packages:write scope and add it as a
|
||||
# repository secret named TKNTKN (Settings → Secrets)
|
||||
password: ${{ secrets.TKNTKN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.meta.outputs.image }}:latest
|
||||
${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.short_sha }}
|
||||
Reference in New Issue
Block a user