diff options
| author | cirakg <cirakg@cirakg.xyz> | 2026-10-18 21:10:37 +0200 |
|---|---|---|
| committer | cirakg <cirakg@cirakg.xyz> | 2026-10-18 21:10:37 +0200 |
| commit | 85f1ecabd7f58f306b2a042376209ac8cf49ab9b (patch) | |
| tree | 00a0f3e2717e5c20ce0c427a979ef609a400d326 /docker/scripts/ciq-env.sh | |
| parent | 2071e2beda763137416ac386d4bb953711ec995d (diff) | |
build: complete the Docker pipeline for build, test, screenshots and packaging
- Container scripts now run from the mounted project (no image rebuild
per script change): build-app.sh (strict -l 3, warnings fail),
run-unit-tests.sh, screenshots.sh, app-diag.sh (perf/memory),
lint.sh, package.sh and sim-vnc.sh (noVNC on 127.0.0.1:6080).
- Headless simulator helpers: wait for port 1234, remove the stale
single-instance lock (~/Sim-dev) that made later starts hang, show
the simulator log on failure.
- New targets: screenshots-approve, icons; `make lock` re-adds the
requirements header; pytest treats warnings as errors.
- Author/Summary headers on every build file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docker/scripts/ciq-env.sh')
| -rwxr-xr-x | docker/scripts/ciq-env.sh | 93 |
1 files changed, 92 insertions, 1 deletions
diff --git a/docker/scripts/ciq-env.sh b/docker/scripts/ciq-env.sh index 945d59a..a52db78 100755 --- a/docker/scripts/ciq-env.sh +++ b/docker/scripts/ciq-env.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash -# Shared helpers for the container scripts. Source this file; don't run it. +# Author: cirakg & claude ai +# Summary: Shared helpers for the container scripts (SDK and key lookup, headless display and simulator). +# +# Source this file; don't run it. # # Paths, as seen inside the container: # /work the project folder (bind mount) @@ -12,6 +15,12 @@ KEY_DIR="/work/.cache/keys" DEV_KEY="${KEY_DIR}/developer_key.der" BUILD_DIR="/work/build" +# Virtual display for the headless simulator, and the TCP port (1234, hex +# 04D2 as in /proc/net/tcp) on which the simulator accepts monkeydo. +SIM_DISPLAY=":99" +SIM_PORT_HEX="04D2" +SIM_START_TIMEOUT_S=60 + # Print an error and stop. Used for every user-facing failure, so the message # says what to do next instead of leaving a bare stack trace. die() { @@ -47,3 +56,85 @@ ciq_require_device() { [[ -f "${CIQ_HOME}/Devices/${device}/compiler.json" ]] \ || die "No device files for '${device}'. Is it in manifest.xml? Then run 'make sdk' to download them." } + +# Start a virtual X display (Xvfb) with the openbox window manager. The window +# manager matters: screenshots and scripted input find the simulator window +# through it. +ciq_start_display() { + export DISPLAY="${SIM_DISPLAY}" + Xvfb "${DISPLAY}" -screen 0 1600x1200x24 -nolisten tcp >/tmp/xvfb.log 2>&1 & + local _ + for _ in $(seq 1 50); do + xdpyinfo >/dev/null 2>&1 && break + sleep 0.1 + done + xdpyinfo >/dev/null 2>&1 || die "Xvfb did not start (see /tmp/xvfb.log)." + openbox >/tmp/openbox.log 2>&1 & +} + +# True when something listens on the simulator's monkeydo port. +ciq_simulator_listening() { + awk -v port=":${SIM_PORT_HEX}" \ + 'NR > 1 && $4 == "0A" && substr($2, length($2) - 4) == port { found = 1 } END { exit !found }' \ + /proc/net/tcp +} + +# Start the Connect IQ simulator (needs ciq_require_sdk and ciq_start_display) +# and wait until it accepts monkeydo connections. +ciq_start_simulator() { + # The simulator's single-instance lock (~/Sim-<user>) stores its process ID + # and survives in .cache/home after the container is gone. A later container + # often reuses that process ID for something else, and the simulator then + # believes it is already running and never opens its port. Each container + # runs exactly one simulator, so the stale lock is safe to remove. + rm -f "${HOME}"/Sim-* + simulator >/tmp/simulator.log 2>&1 & + local pid=$! _ + for _ in $(seq 1 $((SIM_START_TIMEOUT_S * 5))); do + ciq_simulator_listening && return 0 + kill -0 "${pid}" 2>/dev/null || break + sleep 0.2 + done + # The container (and /tmp) disappears after a failure, so show the log now. + echo "--- last lines of the simulator log ---" >&2 + tail -n 25 /tmp/simulator.log >&2 || true + die "The Connect IQ simulator did not start within ${SIM_START_TIMEOUT_S} s." +} + +# The simulator's main window ID, for screenshots and scripted input. +ciq_simulator_window() { + xdotool search --name '^CIQ Simulator' | head -n 1 +} + +# Longest wait for an app's first frame after launching it. +FIRST_FRAME_TIMEOUT_S=60 + +# Launch the debug build of the app on DEVICE and wait for its first frame, +# which the app reports with a "DIAG first_frame" console line. The console +# goes to LOG, followed by a "HARNESS first_frame wall_ms=N" line: the +# wall-clock time from launch to first frame, including the JVM start of +# monkeydo and the program load the app can't time itself. The app keeps +# running (for screenshots) until ciq_stop_app or the next launch. +# Returns 1 if no first frame appeared in time. +ciq_launch_app() { + local device="$1" app_log="$2" start _ + start="$(date +%s%3N)" + monkeydo "${BUILD_DIR}/debug/${device}.prg" "${device}" >"${app_log}" 2>&1 & + CIQ_MONKEYDO_PID=$! + for _ in $(seq 1 $((FIRST_FRAME_TIMEOUT_S * 20))); do + if grep -q "DIAG first_frame" "${app_log}"; then + echo "HARNESS first_frame wall_ms=$(($(date +%s%3N) - start))" >>"${app_log}" + return 0 + fi + # monkeydo only exits on its own when the app could not run. + kill -0 "${CIQ_MONKEYDO_PID}" 2>/dev/null || break + sleep 0.05 + done + return 1 +} + +# Stop the monkeydo session started by ciq_launch_app. +ciq_stop_app() { + kill "${CIQ_MONKEYDO_PID:-0}" 2>/dev/null || true + wait "${CIQ_MONKEYDO_PID:-0}" 2>/dev/null || true +} |
