-
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathtest-scenario.sh
More file actions
executable file
·665 lines (616 loc) · 23.2 KB
/
test-scenario.sh
File metadata and controls
executable file
·665 lines (616 loc) · 23.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
#!/usr/bin/env bash
set -euo pipefail
# GitHub Actions Windows runners often provide `python` but not `python3` on PATH.
# Normalize so the rest of the script can keep using `python3`.
if ! command -v python3 >/dev/null 2>&1; then
if command -v python >/dev/null 2>&1; then
python3() { python "$@"; }
else
echo "Missing required tool: python3 (or python)" >&2
exit 1
fi
fi
usage() {
cat <<'EOF' >&2
Usage: ./test-scenario.sh [options] <scenario.yaml>
Options:
--webrtc-flow Run fast 2-peer WebRTC/hotlink transport smoke scenario
--client-mode MODE SyftBox client: go|rust|mixed|embedded (default: rust)
--sandbox DIR Sandbox root (default: ./sandbox)
--rust-client-bin P Path to Rust client binary (optional)
--skip-rust-build Do not build Rust client (requires binary exists)
--no-reset Do not reset devstack/sandbox (reuse existing state)
--force Force scenario steps to re-run (overrides skip-done)
--allele-count N Override allele-freq synthetic file count (default: 10)
--syqure-agg MODE Syqure aggregation mode: smpc|he (default: smpc)
--syqure-transport T Syqure transport: hotlink|file (default: hotlink)
--hotlink-p2p-only Force p2p-only hotlink mode (disable websocket fallback)
--hotlink-quic-only Deprecated alias for --hotlink-p2p-only
--docker Force Docker mode for syqure runtime
--podman Force Podman runtime (sets BIOVAULT_CONTAINER_RUNTIME=podman)
--keep-containers Keep syqure containers on failure (for logs/debugging)
--set KEY=VALUE Override scenario variables (repeatable)
-h, --help Show this message
Examples:
./test-scenario.sh --webrtc-flow
./test-scenario.sh tests/scenarios/inbox-ping-pong.yaml
./test-scenario.sh --client-mode go tests/scenarios/inbox-ping-pong.yaml
./test-scenario.sh --sandbox sandbox-rs tests/scenarios/inbox-ping-pong.yaml
./test-scenario.sh --client-mode embedded tests/scenarios/inbox-ping-pong.yaml
./test-scenario.sh --docker tests/scenarios/syqure-distributed.yaml
./test-scenario.sh --podman tests/scenarios/syqure-distributed.yaml
./test-scenario.sh --podman --keep-containers tests/scenarios/syqure-distributed.yaml
./test-scenario.sh --syqure-agg he tests/scenarios/syqure-distributed.yaml
./test-scenario.sh --syqure-transport file tests/scenarios/syqure-distributed.yaml
./test-scenario.sh --hotlink-p2p-only tests/scenarios/syqure-distributed.yaml
./test-scenario.sh --no-reset tests/scenarios/allele-freq-syqure.yaml
EOF
}
CLIENT_MODE="rust"
SANDBOX_DIR=""
RUST_CLIENT_BIN=""
SKIP_RUST_BUILD=0
USE_DOCKER=0
USE_PODMAN=0
KEEP_CONTAINERS=0
NO_RESET=0
FORCE_RUN=0
ALLELE_COUNT=""
SYQURE_AGG_MODE="smpc"
SYQURE_TRANSPORT="hotlink"
HOTLINK_P2P_ONLY=0
WEBRTC_FLOW=0
SCENARIO=""
SCENARIO_VARS=()
while [[ $# -gt 0 ]]; do
case "$1" in
--client-mode)
[[ $# -lt 2 ]] && { echo "Missing value for --client-mode" >&2; usage; exit 1; }
CLIENT_MODE="${2:-}"
shift
;;
--sandbox)
[[ $# -lt 2 ]] && { echo "Missing value for --sandbox" >&2; usage; exit 1; }
SANDBOX_DIR="${2:-}"
shift
;;
--rust-client-bin)
[[ $# -lt 2 ]] && { echo "Missing value for --rust-client-bin" >&2; usage; exit 1; }
RUST_CLIENT_BIN="${2:-}"
shift
;;
--skip-rust-build)
SKIP_RUST_BUILD=1
;;
--no-reset)
NO_RESET=1
;;
--force)
FORCE_RUN=1
;;
--allele-count)
[[ $# -lt 2 ]] && { echo "Missing value for --allele-count" >&2; usage; exit 1; }
ALLELE_COUNT="${2:-}"
shift
;;
--syqure-agg)
[[ $# -lt 2 ]] && { echo "Missing value for --syqure-agg" >&2; usage; exit 1; }
SYQURE_AGG_MODE="${2:-}"
shift
;;
--syqure-transport)
[[ $# -lt 2 ]] && { echo "Missing value for --syqure-transport" >&2; usage; exit 1; }
SYQURE_TRANSPORT="${2:-}"
shift
;;
--hotlink-p2p-only|--hotlink-quic-only)
HOTLINK_P2P_ONLY=1
;;
--webrtc-flow)
WEBRTC_FLOW=1
;;
--docker)
USE_DOCKER=1
;;
--podman)
USE_PODMAN=1
USE_DOCKER=1
;;
--keep-containers)
KEEP_CONTAINERS=1
;;
--set)
[[ $# -lt 2 ]] && { echo "Missing value for --set" >&2; usage; exit 1; }
SCENARIO_VARS+=("$2")
shift
;;
-h|--help)
usage
exit 0
;;
-*)
echo "Unknown option: $1" >&2
usage
exit 1
;;
*)
SCENARIO="$1"
;;
esac
shift
done
if [[ -z "$SCENARIO" ]]; then
if (( WEBRTC_FLOW )); then
SCENARIO="tests/scenarios/hotlink-tcp-smoke.yaml"
else
usage
exit 1
fi
fi
if (( WEBRTC_FLOW )); then
if [[ "$SCENARIO" != "tests/scenarios/hotlink-tcp-smoke.yaml" ]]; then
echo "--webrtc-flow cannot be combined with an explicit scenario path" >&2
exit 1
fi
# Force hotlink + p2p-only so this exercises WebRTC path without ws fallback.
SYQURE_TRANSPORT="hotlink"
HOTLINK_P2P_ONLY=1
fi
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "Building BioVault CLI (release)..."
(cd "$ROOT_DIR/cli" && cargo build --release)
echo "Running scenario: $SCENARIO"
# Preserve user JAVA_HOME/PATH for downstream shells (after sbenv activation)
export SCENARIO_JAVA_HOME="${JAVA_HOME:-}"
export SCENARIO_USER_PATH="$PATH"
if command -v nextflow >/dev/null 2>&1; then
export BIOVAULT_BUNDLED_NEXTFLOW="$(command -v nextflow)"
fi
if [[ -n "$SANDBOX_DIR" ]]; then
export SANDBOX_DIR="$SANDBOX_DIR"
fi
if (( USE_PODMAN )); then
export BIOVAULT_CONTAINER_RUNTIME="podman"
export CONTAINERS_MACHINE_PROVIDER="${CONTAINERS_MACHINE_PROVIDER:-hyperv}"
export BIOVAULT_HYPERV_MOUNT="${BIOVAULT_HYPERV_MOUNT:-1}"
fi
if (( KEEP_CONTAINERS )); then
export BIOVAULT_SYQURE_KEEP_CONTAINERS="1"
fi
if (( USE_DOCKER )); then
# Ensure syqure.yaml switches into Docker mode when --docker/--podman is used.
export SEQURE_MODE="${SEQURE_MODE:-docker}"
hotlink_direct=0
if [[ "$SYQURE_TRANSPORT" == "hotlink" ]]; then
case "${BV_SYQURE_DOCKER_DIRECT:-1}" in
0|false|False|FALSE|no|No|NO|off|Off|OFF)
hotlink_direct=0
;;
*)
hotlink_direct=1
export BV_SYQURE_DOCKER_DIRECT=1
;;
esac
fi
if (( hotlink_direct )); then
echo "Syqure Docker direct TCP mode enabled (shared network, proxy bypassed)."
else
# Legacy hotlink path uses host TCP proxy.
# Bind proxy listeners to all interfaces unless user provided an override.
export BV_SYFTBOX_HOTLINK_TCP_PROXY_ADDR="${BV_SYFTBOX_HOTLINK_TCP_PROXY_ADDR:-0.0.0.0}"
# Resolve a numeric host IP from inside Docker so syqure containers avoid
# hostname/address-family issues when connecting to hotlink TCP proxies.
if [[ -z "${BV_SYQURE_CP_HOST:-}" ]] && [[ "${BIOVAULT_CONTAINER_RUNTIME:-docker}" == "docker" ]]; then
detect_docker_host_ip() {
local alias="$1"
docker run --rm alpine:3.19 sh -lc \
"getent hosts '$alias' 2>/dev/null | awk 'NR==1 {print \$1}'" 2>/dev/null || true
}
detect_docker_bridge_gateway() {
docker network inspect bridge --format '{{(index .IPAM.Config 0).Gateway}}' 2>/dev/null || true
}
for alias in host.docker.internal gateway.docker.internal; do
candidate="$(detect_docker_host_ip "$alias")"
if [[ "$candidate" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
export BV_SYQURE_CP_HOST="$candidate"
echo "Syqure Docker CP host: ${BV_SYQURE_CP_HOST} (${alias})"
break
fi
done
# Linux engines may not provide host.docker.internal aliases.
# Fall back to the default bridge gateway when alias lookup is unavailable.
if [[ -z "${BV_SYQURE_CP_HOST:-}" ]]; then
candidate="$(detect_docker_bridge_gateway)"
if [[ "$candidate" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
export BV_SYQURE_CP_HOST="$candidate"
echo "Syqure Docker CP host: ${BV_SYQURE_CP_HOST} (bridge gateway)"
fi
fi
fi
# Fail-fast: verify container→host networking works before running the full test.
if [[ -n "${BV_SYQURE_CP_HOST:-}" ]]; then
# Hard fail fast when container routing to host proxy is impossible.
if ! docker run --rm --platform linux/amd64 alpine:3.19 sh -c \
"ip route get '${BV_SYQURE_CP_HOST}' >/dev/null 2>&1"; then
echo "ERROR: Docker cannot route to Syqure host proxy ${BV_SYQURE_CP_HOST} from containers." >&2
echo "Set BV_SYQURE_CP_HOST to a reachable host IP (for this engine, bridge gateway often works)." >&2
exit 1
fi
echo "Docker networking preflight: checking container→host connectivity (${BV_SYQURE_CP_HOST})..."
# Spin up a tiny container that tries to ping or connect to the host IP.
if docker run --rm --platform linux/amd64 alpine:3.19 sh -c \
"nc -z -w3 '${BV_SYQURE_CP_HOST}' 80 2>/dev/null; ping -c1 -W2 '${BV_SYQURE_CP_HOST}' >/dev/null 2>&1" 2>/dev/null; then
echo "Docker networking preflight: host ${BV_SYQURE_CP_HOST} is reachable ✓"
else
# Ping may fail in some configs (e.g., firewall), but as long as we can
# resolve the host IP the routing should work once proxy ports are listening.
echo "Docker networking preflight: host ${BV_SYQURE_CP_HOST} ping failed (OK if firewall blocks ICMP)"
fi
else
if [[ "$SYQURE_TRANSPORT" == "hotlink" ]]; then
echo "ERROR: Could not detect Docker host IP for Syqure hotlink TCP proxy." >&2
echo "Set BV_SYQURE_CP_HOST explicitly to a reachable host IP and retry." >&2
exit 1
fi
echo "WARNING: Could not detect Docker host IP. Container→host proxy connectivity may fail." >&2
fi
fi
fi
if (( NO_RESET )); then
export BV_DEVSTACK_RESET=0
fi
if (( FORCE_RUN )); then
export BV_FLOW_FORCE=1
fi
if [[ -n "$ALLELE_COUNT" ]]; then
export ALLELE_FREQ_COUNT="$ALLELE_COUNT"
fi
if [[ -n "$SYQURE_AGG_MODE" ]]; then
case "$SYQURE_AGG_MODE" in
smpc|he)
export BV_SYQURE_AGG_MODE="$SYQURE_AGG_MODE"
;;
*)
echo "Unknown --syqure-agg mode: $SYQURE_AGG_MODE (expected smpc|he)" >&2
exit 1
;;
esac
fi
if [[ -n "$SYQURE_TRANSPORT" ]]; then
if [[ "$SYQURE_TRANSPORT" == "webrtc" ]]; then
echo "NOTE: --syqure-transport webrtc is deprecated; using hotlink" >&2
SYQURE_TRANSPORT="hotlink"
fi
case "$SYQURE_TRANSPORT" in
hotlink)
export BV_SYQURE_TRANSPORT="hotlink"
export BV_SYQURE_TCP_PROXY="1"
export BV_SYFTBOX_HOTLINK="1"
export BV_SYFTBOX_HOTLINK_TCP_PROXY="1"
export BV_SYFTBOX_HOTLINK_QUIC="${BV_SYFTBOX_HOTLINK_QUIC:-1}"
if (( HOTLINK_P2P_ONLY )); then
export BV_SYFTBOX_HOTLINK_P2P_ONLY="1"
export BV_SYFTBOX_HOTLINK_QUIC_ONLY="1"
else
export BV_SYFTBOX_HOTLINK_P2P_ONLY="${BV_SYFTBOX_HOTLINK_P2P_ONLY:-0}"
export BV_SYFTBOX_HOTLINK_QUIC_ONLY="${BV_SYFTBOX_HOTLINK_QUIC_ONLY:-$BV_SYFTBOX_HOTLINK_P2P_ONLY}"
fi
;;
file)
export BV_SYQURE_TRANSPORT="file"
export BV_SYQURE_TCP_PROXY="0"
export BV_SYFTBOX_HOTLINK="0"
export BV_SYFTBOX_HOTLINK_TCP_PROXY="0"
export BV_SYFTBOX_HOTLINK_P2P_ONLY="0"
export BV_SYFTBOX_HOTLINK_QUIC="0"
export BV_SYFTBOX_HOTLINK_QUIC_ONLY="0"
;;
*)
echo "Unknown --syqure-transport: $SYQURE_TRANSPORT (expected hotlink|file)" >&2
exit 1
;;
esac
fi
# Let tests/scripts/devstack.sh decide which syftbox client to run.
export BV_DEVSTACK_CLIENT_MODE="$CLIENT_MODE"
if [[ -n "$RUST_CLIENT_BIN" ]]; then
export BV_DEVSTACK_RUST_CLIENT_BIN="$RUST_CLIENT_BIN"
fi
if (( SKIP_RUST_BUILD )); then
export BV_DEVSTACK_SKIP_RUST_BUILD=1
fi
if [[ "${CLIENT_MODE}" == "embedded" ]]; then
# Ensure BioVault-hosted SyftBox runs in embedded mode when started by devstack.sh.
export BV_SYFTBOX_BACKEND=embedded
fi
# Syqure runtime setup - only if scenario needs it (and not on Windows)
# Detect Windows (Git Bash / MSYS / Cygwin)
IS_WINDOWS=0
if [[ "$(uname -s)" == MINGW* ]] || [[ "$(uname -s)" == MSYS* ]] || [[ "$(uname -s)" == CYGWIN* ]]; then
IS_WINDOWS=1
export BV_DEVSTACK_NO_TURN=1
fi
# Check if scenario needs syqure or container runtime
NEEDS_SYQURE=0
if grep -qiE '(syqure|mpc|sequre)' "$SCENARIO" 2>/dev/null; then
NEEDS_SYQURE=1
fi
NEEDS_CONTAINER=0
if grep -qiE '(syqure|mpc|sequre|nextflow|workflow\.nf|bv submit)' "$SCENARIO" 2>/dev/null; then
NEEDS_CONTAINER=1
fi
if (( IS_WINDOWS )) && (( NEEDS_CONTAINER )); then
RUNTIME_PREF="${BIOVAULT_CONTAINER_RUNTIME:-}"
if (( USE_PODMAN )); then
RUNTIME_PREF="podman"
elif (( USE_DOCKER )); then
RUNTIME_PREF="docker"
fi
check_docker() {
command -v docker >/dev/null 2>&1 || return 1
docker info >/dev/null 2>&1
}
check_podman() {
command -v podman >/dev/null 2>&1 || return 1
podman info >/dev/null 2>&1
}
if [[ "$RUNTIME_PREF" == "podman" ]] || { [[ -z "$RUNTIME_PREF" ]] && check_podman && ! check_docker; }; then
export BIOVAULT_CONTAINER_RUNTIME="podman"
export CONTAINERS_MACHINE_PROVIDER="${CONTAINERS_MACHINE_PROVIDER:-hyperv}"
# Hyper-V mounts are unreliable on Windows; force VM-copy mode.
export BIOVAULT_HYPERV_MOUNT="0"
fi
case "$RUNTIME_PREF" in
podman)
if ! check_podman; then
echo "Podman is required for this scenario but is not running. Start it with 'podman machine start'." >&2
exit 1
fi
;;
docker)
if ! check_docker; then
echo "Docker is required for this scenario but is not running. Start Docker Desktop." >&2
exit 1
fi
;;
*)
if check_docker; then
: # ok
elif check_podman; then
: # ok
else
echo "This scenario requires a container runtime (Docker or Podman), but neither is running." >&2
exit 1
fi
;;
esac
fi
if (( NEEDS_SYQURE )); then
# Determine syqure directory
if [[ -n "${BV_SYQURE_DIR:-}" ]]; then
SYQURE_DIR="$BV_SYQURE_DIR"
elif [[ -d "$ROOT_DIR/../syqure" ]]; then
SYQURE_DIR="$ROOT_DIR/../syqure"
else
SYQURE_DIR="$ROOT_DIR/syqure"
fi
SYQURE_BIN_DEBUG="$SYQURE_DIR/target/debug/syqure"
SYQURE_BIN_RELEASE="$SYQURE_DIR/target/release/syqure"
SYQURE_BIN="$SYQURE_BIN_DEBUG"
if (( IS_WINDOWS )); then
# Syqure can't build on Windows - use Docker mode
export BV_SYQURE_USE_DOCKER=1
echo "Syqure mode: Docker (Windows - native build not supported)"
elif (( USE_DOCKER )); then
export BV_SYQURE_USE_DOCKER=1
echo "Syqure mode: Docker"
else
# Preflight: if no bundle is available for native syqure, fall back to Docker.
BUNDLE_OK=0
HAS_PREBUILT_CODON=0
if [[ -n "${SYQURE_BUNDLE_FILE:-}" && -f "${SYQURE_BUNDLE_FILE}" ]]; then
BUNDLE_OK=1
else
if command -v rustc >/dev/null 2>&1; then
HOST_TRIPLE="$(rustc -vV | sed -n 's/^host: //p')"
if [[ -n "$HOST_TRIPLE" && -f "$SYQURE_DIR/bundles/${HOST_TRIPLE}.tar.zst" ]]; then
BUNDLE_OK=1
fi
if [[ -n "$HOST_TRIPLE" && -f "$SYQURE_DIR/syqure/bundles/${HOST_TRIPLE}.tar.zst" ]]; then
BUNDLE_OK=1
fi
fi
if [[ -d "$SYQURE_DIR/bin/macos-arm64/codon" || -d "$SYQURE_DIR/bin/macos-x86_64/codon" || -d "$SYQURE_DIR/bin/linux-x86/codon" || -d "$SYQURE_DIR/bin/linux-arm64/codon" ]]; then
HAS_PREBUILT_CODON=1
fi
if [[ -d "$ROOT_DIR/../codon/install/lib/codon" ]]; then
BUNDLE_OK=1
fi
fi
if [[ "${SYQURE_FORCE_BUNDLE_REBUILD:-0}" == "1" ]]; then
BUNDLE_OK=0
fi
if (( ! BUNDLE_OK )); then
if [[ -d "$SYQURE_DIR/.git" && -f "$SYQURE_DIR/.gitmodules" ]]; then
echo "Syqure bundle/assets missing; initializing submodules..."
(cd "$SYQURE_DIR" && git submodule update --init --recursive)
fi
if (( HAS_PREBUILT_CODON )) && [[ -x "$SYQURE_DIR/syqure_bins.sh" ]]; then
echo "Building syqure bundle from prebuilts (syqure_bins.sh)..."
(cd "$SYQURE_DIR" && ./syqure_bins.sh)
elif [[ -x "$SYQURE_DIR/build_libs.sh" ]]; then
echo "Building syqure bundle (build_libs.sh)..."
(cd "$SYQURE_DIR" && ./build_libs.sh)
fi
# Re-check after attempted build.
if [[ -n "${SYQURE_BUNDLE_FILE:-}" && -f "${SYQURE_BUNDLE_FILE}" ]]; then
BUNDLE_OK=1
else
if command -v rustc >/dev/null 2>&1; then
HOST_TRIPLE="$(rustc -vV | sed -n 's/^host: //p')"
if [[ -n "$HOST_TRIPLE" && -f "$SYQURE_DIR/bundles/${HOST_TRIPLE}.tar.zst" ]]; then
BUNDLE_OK=1
fi
if [[ -n "$HOST_TRIPLE" && -f "$SYQURE_DIR/syqure/bundles/${HOST_TRIPLE}.tar.zst" ]]; then
BUNDLE_OK=1
fi
fi
fi
if (( ! BUNDLE_OK )); then
USE_DOCKER=1
export BV_SYQURE_USE_DOCKER=1
echo "Syqure bundle not found; falling back to Docker. Set SYQURE_BUNDLE_FILE to use native."
fi
fi
fi
if (( USE_DOCKER )); then
export BV_SYQURE_USE_DOCKER=1
echo "Syqure mode: Docker"
else
# Native mode - build syqure if needed
if [[ ! -x "$SYQURE_BIN" ]]; then
if [[ -d "$SYQURE_DIR" ]]; then
echo "Building syqure native binary (CI-style)..."
# Mirror syqure CI smoke build: use precompiled Codon from bin/<platform>/codon if present.
SYQURE_PLATFORM=""
OS_NAME="$(uname -s | tr '[:upper:]' '[:lower:]')"
ARCH_NAME="$(uname -m | tr '[:upper:]' '[:lower:]')"
case "$OS_NAME" in
darwin) OS_LABEL="macos" ;;
linux) OS_LABEL="linux" ;;
*) OS_LABEL="$OS_NAME" ;;
esac
case "$ARCH_NAME" in
arm64|aarch64) ARCH_LABEL="arm64" ;;
x86_64|amd64|i386|i686) ARCH_LABEL="x86" ;;
*) ARCH_LABEL="$ARCH_NAME" ;;
esac
SYQURE_PLATFORM="${OS_LABEL}-${ARCH_LABEL}"
BIN_ROOT="$SYQURE_DIR/bin/$SYQURE_PLATFORM/codon"
if [[ -d "$BIN_ROOT" ]]; then
export SYQURE_CPP_INCLUDE="$BIN_ROOT/include"
export SYQURE_CPP_LIB_DIRS="$BIN_ROOT/lib/codon"
# Fix broken libgmp symlink in precompiled bundle if needed (macOS).
if [[ "$OS_LABEL" == "macos" ]]; then
GMP_FILE="$BIN_ROOT/lib/codon/libgmp.dylib"
if [[ -L "$GMP_FILE" && ! -e "$GMP_FILE" ]]; then
echo "Repairing broken libgmp symlink in $BIN_ROOT..."
GMP_SRC=""
if command -v brew >/dev/null 2>&1; then
GMP_PREFIX="$(brew --prefix gmp 2>/dev/null || true)"
if [[ -n "$GMP_PREFIX" && -f "$GMP_PREFIX/lib/libgmp.dylib" ]]; then
GMP_SRC="$GMP_PREFIX/lib/libgmp.dylib"
fi
fi
if [[ -z "$GMP_SRC" ]]; then
for candidate in /opt/homebrew/opt/gmp/lib/libgmp.dylib /usr/local/opt/gmp/lib/libgmp.dylib; do
if [[ -f "$candidate" ]]; then
GMP_SRC="$candidate"
break
fi
done
fi
if [[ -n "$GMP_SRC" ]]; then
rm -f "$BIN_ROOT/lib/codon/libgmp.dylib" "$BIN_ROOT/lib/codon/libgmp.so"
cp -L "$GMP_SRC" "$BIN_ROOT/lib/codon/libgmp.dylib"
cp -L "$GMP_SRC" "$BIN_ROOT/lib/codon/libgmp.so"
else
echo "libgmp.dylib not found; install gmp or set SEQURE_GMP_PATH" >&2
exit 1
fi
fi
fi
fi
(cd "$SYQURE_DIR" && cargo build -p syqure) || {
echo "Failed to build syqure. Use --docker flag for Docker mode." >&2
exit 1
}
else
echo "Syqure directory not found at $SYQURE_DIR. Use --docker flag for Docker mode." >&2
exit 1
fi
fi
# Prefer release binary for runtime stability unless explicitly disabled.
if [[ "${BV_SYQURE_PREFER_RELEASE:-1}" == "1" ]]; then
if [[ ! -x "$SYQURE_BIN_RELEASE" && -d "$SYQURE_DIR" ]]; then
echo "Building syqure native binary (release)..."
(cd "$SYQURE_DIR" && cargo build -p syqure --release) || {
echo "Failed to build syqure (release). Falling back to debug binary." >&2
}
fi
if [[ -x "$SYQURE_BIN_RELEASE" ]]; then
export SEQURE_NATIVE_BIN="$SYQURE_BIN_RELEASE"
fi
fi
if [[ -z "${SEQURE_NATIVE_BIN:-}" && -x "$SYQURE_BIN_DEBUG" ]]; then
export SEQURE_NATIVE_BIN="$SYQURE_BIN_DEBUG"
fi
if [[ -x "${SEQURE_NATIVE_BIN:-}" ]]; then
echo "Syqure mode: Native ($SEQURE_NATIVE_BIN)"
fi
fi
else
echo "Syqure mode: Not needed for this scenario"
fi
# Syqure runs can take longer; increase background timeout unless user set it.
if (( NEEDS_SYQURE )); then
if (( IS_WINDOWS )); then
export SCENARIO_BG_TIMEOUT="${SCENARIO_BG_TIMEOUT:-3600}"
else
export SCENARIO_BG_TIMEOUT="${SCENARIO_BG_TIMEOUT:-1200}"
fi
echo "Background process timeout: ${SCENARIO_BG_TIMEOUT}s"
if (( IS_WINDOWS )); then
export SCENARIO_TAIL_SYQURE_LOGS="${SCENARIO_TAIL_SYQURE_LOGS:-1}"
fi
fi
TAIL_PID=""
PROGRESS_PID=""
start_syqure_log_tail() {
if [[ "${SCENARIO_TAIL_SYQURE_LOGS:-0}" != "1" ]]; then
return
fi
if (( ! IS_WINDOWS )); then
return
fi
echo "Tailing syqure file_transport.log (Windows)..."
powershell.exe -NoProfile -Command '& { $ErrorActionPreference = "SilentlyContinue"; $root = (Get-Location).Path + "\\sandbox"; $pos = @{}; while ($true) { Get-ChildItem -Recurse -Filter file_transport.log $root -ErrorAction SilentlyContinue | ForEach-Object { $p = $_.FullName; if (-not $pos.ContainsKey($p)) { $pos[$p] = 0 }; try { $fs = [IO.File]::Open($p, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::ReadWrite); $fs.Seek([long]$pos[$p], [IO.SeekOrigin]::Begin) | Out-Null; $sr = New-Object IO.StreamReader($fs); while (-not $sr.EndOfStream) { $line = $sr.ReadLine(); if ($line) { Write-Host ("[syqure-log] {0}: {1}" -f $p, $line) } }; $pos[$p] = $fs.Position; $sr.Close(); $fs.Close() } catch {} }; Start-Sleep -Seconds 2 } }' &
TAIL_PID=$!
}
stop_syqure_log_tail() {
if [[ -n "${TAIL_PID:-}" ]]; then
kill "$TAIL_PID" >/dev/null 2>&1 || true
fi
if [[ -n "${PROGRESS_PID:-}" ]]; then
kill "$PROGRESS_PID" >/dev/null 2>&1 || true
fi
}
start_syqure_progress_tail() {
if [[ "${SCENARIO_TAIL_SYQURE_LOGS:-0}" != "1" ]]; then
return
fi
if (( ! IS_WINDOWS )); then
return
fi
echo "Tracking syqure message counts (Windows)..."
powershell.exe -NoProfile -Command '& { $ErrorActionPreference = "SilentlyContinue"; $root = (Get-Location).Path + "\\sandbox"; while ($true) { $now = Get-Date -Format "HH:mm:ss"; $groups = Get-ChildItem -Recurse -Filter *.request $root -ErrorAction SilentlyContinue | Group-Object DirectoryName | Sort-Object Count -Descending | Select-Object -First 5; if ($groups) { foreach ($g in $groups) { Write-Host ("[syqure-progress] {0} {1} files in {2}" -f $now, $g.Count, $g.Name) } } else { Write-Host ("[syqure-progress] {0} no request files yet" -f $now) }; Start-Sleep -Seconds 10 } }' &
PROGRESS_PID=$!
}
SCENARIO_ARGS=()
for kv in ${SCENARIO_VARS[@]+"${SCENARIO_VARS[@]}"}; do
SCENARIO_ARGS+=(--set "$kv")
done
if python3 -c 'import yaml' >/dev/null 2>&1; then
start_syqure_log_tail
start_syqure_progress_tail
python3 "$ROOT_DIR/scripts/run_scenario.py" "$SCENARIO" ${SCENARIO_ARGS[@]+"${SCENARIO_ARGS[@]}"}
status=$?
stop_syqure_log_tail
exit $status
fi
start_syqure_log_tail
start_syqure_progress_tail
python3 "$ROOT_DIR/scripts/run_scenario.py" "$SCENARIO" ${SCENARIO_ARGS[@]+"${SCENARIO_ARGS[@]}"}
status=$?
stop_syqure_log_tail
exit $status