4444 chmod +x scripts/version-check.sh
4545 ./scripts/version-check.sh
4646
47- - name : Test versions-up build args
48- shell : bash
49- run : bash ./tests/version-upgrade.sh
47+ - name : Unit tests (release-utils.sh)
48+ run : bash tests/test_release_utils.sh
49+
50+ - name : Unit tests (version-upgrade.sh)
51+ run : bash tests/version-upgrade.sh
5052
5153 smoke :
5254 name : Installer Smoke Test
@@ -55,25 +57,30 @@ jobs:
5557 - name : Checkout
5658 uses : actions/checkout@v4
5759
58- - name : Setup Node
59- uses : actions/setup-node@v4
60- with :
61- node-version : " 24"
62-
63- - name : Resolve tool versions
64- id : versions
65- env :
66- GH_TOKEN : ${{ github.token }}
67- run : bash ./scripts/resolve-tool-versions.sh
60+ - name : Load pinned versions
61+ id : pins
62+ shell : bash
63+ run : |
64+ set -euo pipefail
65+ source ./scripts/version-pins.sh
66+ load_version_pins
67+ emit_github_outputs "$GITHUB_OUTPUT"
6868
6969 - name : Build local smoke image
7070 run : |
7171 docker build -t deva-smoke:ci \
72- --build-arg CLAUDE_CODE_VERSION="${{ steps.versions.outputs.claude_code_version }}" \
73- --build-arg CODEX_VERSION="${{ steps.versions.outputs.codex_version }}" \
74- --build-arg GEMINI_CLI_VERSION="${{ steps.versions.outputs.gemini_cli_version }}" \
75- --build-arg ATLAS_CLI_VERSION="${{ steps.versions.outputs.atlas_cli_version }}" \
76- --build-arg COPILOT_API_VERSION="${{ steps.versions.outputs.copilot_api_version }}" \
72+ --build-arg NODE_MAJOR="${{ steps.pins.outputs.node_major }}" \
73+ --build-arg GO_VERSION="${{ steps.pins.outputs.go_version }}" \
74+ --build-arg PYTHON_VERSION="${{ steps.pins.outputs.python_version }}" \
75+ --build-arg DELTA_VERSION="${{ steps.pins.outputs.delta_version }}" \
76+ --build-arg TMUX_VERSION="${{ steps.pins.outputs.tmux_version }}" \
77+ --build-arg TMUX_SHA256="${{ steps.pins.outputs.tmux_sha256 }}" \
78+ --build-arg CLAUDE_CODE_VERSION="${{ steps.pins.outputs.claude_code_version }}" \
79+ --build-arg CLAUDE_TRACE_VERSION="${{ steps.pins.outputs.claude_trace_version }}" \
80+ --build-arg CODEX_VERSION="${{ steps.pins.outputs.codex_version }}" \
81+ --build-arg GEMINI_CLI_VERSION="${{ steps.pins.outputs.gemini_cli_version }}" \
82+ --build-arg ATLAS_CLI_VERSION="${{ steps.pins.outputs.atlas_cli_version }}" \
83+ --build-arg COPILOT_API_VERSION="${{ steps.pins.outputs.copilot_api_version }}" \
7784 .
7885
7986 - name : Build core and rust images via Makefile
@@ -83,28 +90,22 @@ jobs:
8390 TAG=ci \
8491 CORE_TAG=ci-core \
8592 RUST_TAG=ci-rust \
86- CLAUDE_CODE_VERSION="${{ steps.versions.outputs.claude_code_version }}" \
87- CODEX_VERSION="${{ steps.versions.outputs.codex_version }}" \
88- GEMINI_CLI_VERSION="${{ steps.versions.outputs.gemini_cli_version }}" \
89- ATLAS_CLI_VERSION="${{ steps.versions.outputs.atlas_cli_version }}" \
90- COPILOT_API_VERSION="${{ steps.versions.outputs.copilot_api_version }}"
91-
92- - name : Smoke rust runtime and browser tooling
93- shell : bash
94- run : |
95- set -euo pipefail
96- docker run --rm deva-smoke:ci-rust bash -lc '
97- bwrap --version
98- go version | grep "go1.26.2"
99- playwright --version
100- playwright-mcp --help >/dev/null
101- playwright install --list
102- if command -v google-chrome >/dev/null 2>&1; then
103- google-chrome --version
104- else
105- echo "google-chrome not installed on $(dpkg --print-architecture)"
106- fi
107- '
93+ NODE_MAJOR="${{ steps.pins.outputs.node_major }}" \
94+ GO_VERSION="${{ steps.pins.outputs.go_version }}" \
95+ PYTHON_VERSION="${{ steps.pins.outputs.python_version }}" \
96+ DELTA_VERSION="${{ steps.pins.outputs.delta_version }}" \
97+ TMUX_VERSION="${{ steps.pins.outputs.tmux_version }}" \
98+ TMUX_SHA256="${{ steps.pins.outputs.tmux_sha256 }}" \
99+ CLAUDE_CODE_VERSION="${{ steps.pins.outputs.claude_code_version }}" \
100+ CLAUDE_TRACE_VERSION="${{ steps.pins.outputs.claude_trace_version }}" \
101+ CODEX_VERSION="${{ steps.pins.outputs.codex_version }}" \
102+ GEMINI_CLI_VERSION="${{ steps.pins.outputs.gemini_cli_version }}" \
103+ ATLAS_CLI_VERSION="${{ steps.pins.outputs.atlas_cli_version }}" \
104+ COPILOT_API_VERSION="${{ steps.pins.outputs.copilot_api_version }}" \
105+ PLAYWRIGHT_VERSION="${{ steps.pins.outputs.playwright_version }}" \
106+ RUST_TOOLCHAINS="${{ steps.pins.outputs.rust_toolchains }}" \
107+ RUST_DEFAULT_TOOLCHAIN="${{ steps.pins.outputs.rust_default_toolchain }}" \
108+ RUST_TARGETS="${{ steps.pins.outputs.rust_targets }}"
108109
109110 - name : Install and launch each agent without a TTY
110111 shell : bash
@@ -148,6 +149,26 @@ jobs:
148149 grep -F -- "$bridge_dir:/deva-host-chrome-bridge" <<<"$dry_run"
149150 grep -F -- "$profile_dir/Extensions:/home/deva/.config/google-chrome/Default/Extensions:ro" <<<"$dry_run"
150151
152+ - name : Smoke bind mount shape guard
153+ shell : bash
154+ run : |
155+ set -euo pipefail
156+ DEVA_DOCKER_IMAGE=deva-smoke \
157+ DEVA_DOCKER_TAG=ci \
158+ ./scripts/test-mount-shape.sh
159+
160+ - name : Smoke agent tooling installer
161+ shell : bash
162+ run : |
163+ set -euo pipefail
164+ ./scripts/test-install-agent-tooling.sh
165+
166+ - name : Smoke version targets
167+ shell : bash
168+ run : |
169+ set -euo pipefail
170+ ./scripts/test-version-targets.sh
171+
151172 - name : Smoke Chrome bridge entrypoint symlink
152173 shell : bash
153174 run : |
@@ -168,6 +189,44 @@ jobs:
168189 deva-smoke:ci \
169190 bash -lc 'link="/tmp/claude-mcp-browser-bridge-$(id -un)"; test -L "$link"; test "$(readlink "$link")" = "/deva-host-chrome-bridge"'
170191
192+ - name : Smoke tmux-bridge CLI surface
193+ shell : bash
194+ run : |
195+ set -euo pipefail
196+ # tmux-bridge is the layer-2 agent comms CLI vendored from smux.
197+ # We assert it's installed, executable, runs outside a tmux pane for
198+ # the version/help/id error paths, and that tmux itself is present
199+ # so a future operator can drive tmux-bridge against host tmux.
200+ docker run --rm deva-smoke:ci bash -lc '
201+ set -euo pipefail
202+ command -v tmux-bridge
203+ command -v tmux
204+ tmux-bridge version
205+ tmux-bridge --help | grep -q "cross-pane communication"
206+ # id requires $TMUX_PANE; outside tmux it must error cleanly.
207+ if tmux-bridge id 2>/dev/null; then
208+ echo "tmux-bridge id should fail outside a tmux pane" >&2
209+ exit 1
210+ fi
211+ # Ephemeral tmux server to prove list/name/read/type work end-to-end.
212+ sock="/tmp/tmux-smoke.sock"
213+ tmux -S "$sock" new-session -d -s smoke "sleep 30"
214+ export TMUX_BRIDGE_SOCKET="$sock"
215+ tmux-bridge list | tee /tmp/list.out
216+ grep -q "smoke:0" /tmp/list.out
217+ target="$(tmux -S "$sock" display-message -p "#{pane_id}")"
218+ tmux-bridge name "$target" smoke-worker
219+ tmux-bridge resolve smoke-worker
220+ tmux-bridge read "$target" 5 >/dev/null
221+ # read-guard: must read before type; second type without read must fail
222+ tmux-bridge type smoke-worker "echo hi"
223+ if tmux-bridge type smoke-worker "echo twice" 2>/dev/null; then
224+ echo "read-guard should have blocked second type without read" >&2
225+ exit 1
226+ fi
227+ tmux -S "$sock" kill-server
228+ '
229+
171230 docs :
172231 name : Docs Build
173232 runs-on : ubuntu-latest
0 commit comments