返回 CodeWhale
record-session.sh
根目录 / scripts / media / record-session.sh
1 #!/usr/bin/env bash
2 # Record the v0.9.2 real-session media (#4906) into a sealed environment.
3 #
4 # This wrapper exists for one reason: a recording is published footage, and the
5 # failure mode is not "the take is bad" — it is "the take contains your home
6 # directory, your shell history, or a credential". Every guard below refuses to
7 # record rather than producing something that has to be scrubbed afterwards.
8 #
9 # It records a REAL session. It does not stage or reconstruct output. If the
10 # take is unimpressive, change the task and re-run; do not edit frames.
11 #
12 # Procedure and acceptance checklist: docs/releases/v0.9.2-media-plan.md
13 # Verify the result with: scripts/media/check-media-assets.py
14 set -euo pipefail
15
16 repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
17 tape="${repo_root}/docs/evidence/v092-first-fleet-session.tape"
18 demo_dir="${CW_MEDIA_DEMO_DIR:-/tmp/cw-media-demo}"
19 out_dir="${CW_MEDIA_OUT_DIR:-${repo_root}/.media-out}"
20
21 die() { echo "ERROR: $*" >&2; exit 1; }
22
23 # --- Preconditions ----------------------------------------------------------
24
25 command -v vhs >/dev/null || die "vhs is not installed (brew install vhs)"
26 command -v ffmpeg >/dev/null || die "ffmpeg is not installed (brew install ffmpeg)"
27 [[ -f "${tape}" ]] || die "missing tape: ${tape}"
28
29 # Record the exact binary that was dogfooded, not whatever is first on PATH.
30 binary="$(command -v codewhale || true)"
31 [[ -n "${binary}" ]] || die "codewhale is not on PATH — run scripts/release/install-dogfood.sh first"
32
33 version_line="$("${binary}" --version)"
34 head_sha="$(git -C "${repo_root}" rev-parse HEAD)"
35 short_sha="${head_sha:0:12}"
36 if [[ "${version_line}" != *"${short_sha}"* ]]; then
37 die "installed codewhale is not this checkout.
38 installed: ${version_line}
39 HEAD: ${short_sha}
40 Recording a SHA that is not the release candidate makes the asset a lie about
41 what the product looks like. Run scripts/release/install-dogfood.sh."
42 fi
43
44 if [[ -n "$(git -C "${repo_root}" status --porcelain --untracked-files=no)" ]]; then
45 die "refusing to record from a dirty tree — the capture receipt could not name an exact source"
46 fi
47
48 # --- Credential guard -------------------------------------------------------
49 # The session must be keyless on a sealed local route. Anything that looks like
50 # a provider credential in the environment could reach the screen.
51 leaked=()
52 while IFS='=' read -r name _; do
53 case "${name}" in
54 *API_KEY|*_TOKEN|*_SECRET|ANTHROPIC_*|OPENAI_*|DEEPSEEK_*|MOONSHOT_*|KIMI_*|GROQ_*|XAI_*|GEMINI_*)
55 leaked+=("${name}") ;;
56 esac
57 done < <(env)
58 if (( ${#leaked[@]} )); then
59 die "provider credentials are present in the environment: ${leaked[*]}
60 Re-run from a clean shell (\`env -i\` or a fresh terminal). The recording must
61 be demonstrably keyless."
62 fi
63
64 # --- Sealed environment -----------------------------------------------------
65 sealed="$(mktemp -d)"
66 mkdir -p "${sealed}/.codewhale" "${demo_dir}" "${out_dir}"
67 cleanup() { rm -rf "${sealed}"; }
68 trap cleanup EXIT
69
70 # Blue Stage dark, selected in the product rather than imitated by the
71 # emulator palette (see the tape's note).
72 cat > "${sealed}/.codewhale/config.toml" <<'TOML'
73 theme = "Blue Stage"
74 TOML
75
76 echo "Recording ${version_line}"
77 echo " tape: ${tape}"
78 echo " workspace: ${demo_dir}"
79 echo " sealed: HOME=${sealed}"
80 echo
81
82 pushd "${demo_dir}" >/dev/null
83 HOME="${sealed}" \
84 CODEWHALE_HOME="${sealed}/.codewhale" \
85 CODEWHALE_CONFIG_PATH="${sealed}/.codewhale/config.toml" \
86 CODEWHALE_MCP_CONFIG="${sealed}/.codewhale/mcp.json" \
87 vhs "${tape}"
88 popd >/dev/null
89
90 gif="${demo_dir}/first-fleet-session.gif"
91 [[ -f "${gif}" ]] || die "vhs produced no GIF at ${gif}"
92
93 # --- Derivatives (media plan step 4) ----------------------------------------
94 mv "${gif}" "${out_dir}/first-fleet-session.gif"
95 gif="${out_dir}/first-fleet-session.gif"
96
97 ffmpeg -loglevel error -y -i "${gif}" -movflags +faststart -pix_fmt yuv420p \
98 -vf "scale=1280:720" -an "${out_dir}/first-fleet-session.mp4"
99 ffmpeg -loglevel error -y -i "${gif}" -frames:v 1 "${out_dir}/first-fleet-session.png"
100
101 # --- Capture receipt --------------------------------------------------------
102 # So the asset can be re-shot when the UI changes, instead of quietly aging
103 # into a lie about what the product looks like.
104 cat > "${out_dir}/capture.json" <<JSON
105 {
106 "id": "first-fleet-session",
107 "issue": "https://github.com/Hmbown/CodeWhale/issues/4906",
108 "recorded_from_commit": "${head_sha}",
109 "version_line": "${version_line}",
110 "tape": "docs/evidence/v092-first-fleet-session.tape",
111 "grid": "120x32",
112 "product_theme": "Blue Stage",
113 "route": "sealed loopback Ollama; no hosted provider, no account, no credential",
114 "workspace": "${demo_dir}",
115 "sha256": {
116 "gif": "$(shasum -a 256 "${out_dir}/first-fleet-session.gif" | cut -d' ' -f1)",
117 "mp4": "$(shasum -a 256 "${out_dir}/first-fleet-session.mp4" | cut -d' ' -f1)",
118 "png": "$(shasum -a 256 "${out_dir}/first-fleet-session.png" | cut -d' ' -f1)"
119 }
120 }
121 JSON
122
123 echo
124 echo "Wrote:"
125 ls -la "${out_dir}"
126 echo
127 echo "Next:"
128 echo " 1. WATCH THE TAKE. If it is not worth showing, change the task and re-run."
129 echo " 2. python3 scripts/media/check-media-assets.py --dir ${out_dir}"
130 echo " 3. Follow the acceptance checklist in docs/releases/v0.9.2-media-plan.md"
131 echo " before copying anything into web/public/media/ or flipping the"
132 echo " manifest entry to published."
133
133 lines BASH