#!/usr/bin/env bash set -u usage() { cat <<'USAGE' Usage: run_matrix.sh [--all|--unit|--cross|--proto] Runs Proto Socket proto schema sync, unit/same-language tests, and/or cross-language communication tests, then prints Markdown PASS/FAIL tables. Each run also writes a local result record under agent-test/runs/. Defaults to --all. USAGE } mode="all" case "${1:-}" in ""|--all) mode="all" ;; --unit) mode="unit" ;; --cross) mode="cross" ;; --proto) mode="proto" ;; -h|--help) usage; exit 0 ;; *) usage >&2; exit 2 ;; esac script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" repo_root="$(cd "$script_dir/../../../../.." && pwd)" log_dir="$(mktemp -d "${TMPDIR:-/tmp}/proto-socket-matrix.XXXXXX")" run_started_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)" run_record_stamp="$(date -u +%Y%m%d-%H%M%S)" remote_web_host="${PROTO_SOCKET_DART_WEB_REMOTE:-toki@toki-labs.com}" remote_web_root_expr="${PROTO_SOCKET_DART_WEB_REMOTE_ROOT:-~/works}" remote_web_repo_name="${PROTO_SOCKET_DART_WEB_REMOTE_REPO:-proto-socket-dart-web}" remote_web_repo_url="${PROTO_SOCKET_DART_WEB_REMOTE_URL:-$(git -C "$repo_root" config --get remote.origin.url 2>/dev/null || true)}" remote_web_ref="${PROTO_SOCKET_DART_WEB_REF:-$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || echo master)}" remote_web_execution="local" remote_web_prepare_summary="" case "$mode" in all) record_profile="proto-socket-full-matrix" ;; unit) record_profile="proto-socket-unit-matrix" ;; cross) record_profile="proto-socket-cross-matrix" ;; proto) record_profile="proto-socket-proto-sync" ;; esac languages=("Dart" "Go" "Kotlin" "Python" "TypeScript") declare -A unit_cmds=( ["Dart"]="dart pub get && dart test && dart compile js test/browser_ws_import_compile.dart -o /tmp/proto_socket_browser_ws_import_compile.js" ["Go"]="go test ./..." ["Kotlin"]="./gradlew test" ["Python"]="python3 -m pytest -q" ["TypeScript"]="npm run check && npm test" ) declare -A unit_dirs=( ["Dart"]="$repo_root/dart" ["Go"]="$repo_root/go" ["Kotlin"]="$repo_root/kotlin" ["Python"]="$repo_root/python" ["TypeScript"]="$repo_root/typescript" ) declare -A unit_result declare -A unit_status declare -A unit_summary proto_cmd="tools/check_proto_sync.sh" proto_dir="$repo_root" proto_result="-" proto_status="" proto_summary="" cross_cases=( "Go|Dart|$repo_root/go|go run ./crosstest/go_dart.go" "Go|Kotlin|$repo_root/go|go run ./crosstest/go_kotlin.go" "Go|Python|$repo_root/go|go run ./crosstest/go_python.go" "Go|TypeScript|$repo_root/go|go run ./crosstest/go_typescript.go" "Dart|Go|$repo_root/dart|dart run crosstest/dart_go.dart" "Dart|Kotlin|$repo_root/dart|dart run crosstest/dart_kotlin.dart" "Dart|Python|$repo_root/dart|dart run crosstest/dart_python.dart" "Dart|TypeScript|$repo_root/dart|dart run crosstest/dart_typescript.dart" "Kotlin|Dart|$repo_root/kotlin|./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinDartKt" "Kotlin|Go|$repo_root/kotlin|./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinGoKt" "Kotlin|Python|$repo_root/kotlin|./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinPythonKt" "Kotlin|TypeScript|$repo_root/kotlin|./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinTypescriptKt" "Python|Dart|$repo_root/python|python3 crosstest/python_dart.py" "Python|Go|$repo_root/python|python3 crosstest/python_go.py" "Python|Kotlin|$repo_root/python|python3 crosstest/python_kotlin.py" "Python|TypeScript|$repo_root/python|python3 crosstest/python_typescript.py" "TypeScript|Dart|$repo_root/typescript|./node_modules/.bin/tsx crosstest/typescript_dart.ts" "TypeScript|Go|$repo_root/typescript|./node_modules/.bin/tsx crosstest/typescript_go.ts" "TypeScript|Kotlin|$repo_root/typescript|./node_modules/.bin/tsx crosstest/typescript_kotlin.ts" "TypeScript|Python|$repo_root/typescript|./node_modules/.bin/tsx crosstest/typescript_python.ts" ) declare -A cross_result declare -A cross_status declare -A cross_pass_lines declare -A cross_fail_lines declare -A cross_cmds expected_cross_pass_lines=16 web_cases=( "Dart.io|$repo_root/dart|dart run crosstest/dart_web.dart" "Go|$repo_root/go|go run ./crosstest/go_dart_web.go" "Kotlin|$repo_root/kotlin|./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinDartWebKt" "Python|$repo_root/python|python3 crosstest/python_dart_web.py" "TypeScript|$repo_root/typescript|./node_modules/.bin/tsx crosstest/typescript_dart_web.ts" ) declare -A web_result declare -A web_ws_result declare -A web_wss_result declare -A web_status declare -A web_pass_lines declare -A web_ws_pass_lines declare -A web_wss_pass_lines declare -A web_fail_lines declare -A web_cmds declare -A web_block_reason expected_web_pass_lines=4 expected_web_transport_pass_lines=2 count_lines() { local pattern="$1" local file="$2" local count if command -v rg >/dev/null 2>&1; then count="$(rg -c "$pattern" "$file" 2>/dev/null || true)" else count="$(grep -Ec "$pattern" "$file" 2>/dev/null || true)" fi echo "${count:-0}" } single_quote() { printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")" } remote_exec() { local cmd="$1" ssh -o BatchMode=yes -o ConnectTimeout=10 "$remote_web_host" "zsh -lc $(single_quote "$cmd")" } run_logged() { local name="$1" local dir="$2" local cmd="$3" local log_file="$4" printf 'RUN %s\n' "$name" >&2 ( cd "$dir" && bash -c "$cmd" ) >"$log_file" 2>&1 } chrome_available() { if [ -n "${CHROME_EXECUTABLE:-}" ] && [ -x "$CHROME_EXECUTABLE" ]; then return 0 fi command -v google-chrome >/dev/null 2>&1 } remote_chrome_export_command() { cat <<'REMOTE_CHROME' if [ -n "${CHROME_EXECUTABLE:-}" ] && [ -x "$CHROME_EXECUTABLE" ]; then : elif [ -n "${repo_dir:-}" ] && [ -x "$repo_dir/.agent-test/chrome-proto-socket-wss" ]; then export CHROME_EXECUTABLE="$repo_dir/.agent-test/chrome-proto-socket-wss" elif [ -x "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" ]; then export CHROME_EXECUTABLE="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" elif command -v google-chrome >/dev/null 2>&1; then export CHROME_EXECUTABLE="$(command -v google-chrome)" elif command -v chromium >/dev/null 2>&1; then export CHROME_EXECUTABLE="$(command -v chromium)" elif command -v chromium-browser >/dev/null 2>&1; then export CHROME_EXECUTABLE="$(command -v chromium-browser)" else echo "Chrome executable not found on remote." >&2 exit 90 fi REMOTE_CHROME } remote_chrome_wrapper_setup_command() { cat <<'REMOTE_CHROME_WRAPPER' if [ -x "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" ] && command -v openssl >/dev/null 2>&1 && [ -f dart/test/certs/server.crt ]; then mkdir -p "$repo_dir/.agent-test" spki="$(openssl x509 -in dart/test/certs/server.crt -pubkey -noout | openssl pkey -pubin -outform DER | openssl dgst -sha256 -binary | openssl base64)" cat > "$repo_dir/.agent-test/chrome-proto-socket-wss" <&2 return 1 fi repo_name_q="$(single_quote "$remote_web_repo_name")" repo_url_q="$(single_quote "$remote_web_repo_url")" repo_ref_q="$(single_quote "$remote_web_ref")" chrome_cmd="$(remote_chrome_export_command)" chrome_wrapper_cmd="$(remote_chrome_wrapper_setup_command)" remote_exec " set -e repo_parent=$remote_web_root_expr repo_name=$repo_name_q repo_url=$repo_url_q repo_ref=$repo_ref_q mkdir -p \"\$repo_parent\" repo_dir=\"\$repo_parent/\$repo_name\" if [ ! -d \"\$repo_dir/.git\" ]; then git clone \"\$repo_url\" \"\$repo_dir\" fi cd \"\$repo_dir\" if ! git diff --quiet || ! git diff --cached --quiet || [ -n \"\$(git ls-files --others --exclude-standard)\" ]; then git stash push -u -m \"proto-socket matrix remote test stash \$(date -u +%Y%m%d-%H%M%S)\" >/dev/null fi git fetch origin --prune git checkout \"\$repo_ref\" git pull --ff-only origin \"\$repo_ref\" (cd dart && dart pub get) if [ -f typescript/package.json ]; then (cd typescript && npm install) fi if [ -f python/pyproject.toml ]; then (cd python && python3 -m pip install -e '.[dev]') fi $chrome_wrapper_cmd $chrome_cmd printf 'remote Dart.web repo: %s\n' \"\$repo_dir\" printf 'remote Dart.web git ref: %s\n' \"\$(git rev-parse --short HEAD)\" " } sync_remote_web_worktree() { local changed_list sync_count changed_list="$log_dir/remote_web_changed_files.zlist" { git -C "$repo_root" diff --name-only -z git -C "$repo_root" diff --cached --name-only -z git -C "$repo_root" ls-files --others --exclude-standard -z } | sort -zu >"$changed_list" if [ ! -s "$changed_list" ]; then return 0 fi sync_count="$(tr '\0' '\n' <"$changed_list" | sed '/^$/d' | wc -l | tr -d ' ')" ( cd "$repo_root" && tar --null -T "$changed_list" -cf - ) | remote_exec " set -e repo_parent=$remote_web_root_expr repo_name=$(single_quote "$remote_web_repo_name") repo_dir=\"\$repo_parent/\$repo_name\" cd \"\$repo_dir\" tar -xf - " remote_web_prepare_summary="${remote_web_prepare_summary} local worktree sync: ${sync_count} files " } run_remote_web_logged() { local server="$1" local dir="$2" local cmd="$3" local log_file="$4" local rel_dir chrome_cmd rel_dir_q rel_dir="${dir#$repo_root/}" rel_dir_q="$(single_quote "$rel_dir")" chrome_cmd="$(remote_chrome_export_command)" printf 'RUN remote web %s->Dart.web: %s\n' "$server" "$cmd" >&2 remote_exec " set -e repo_parent=$remote_web_root_expr repo_name=$(single_quote "$remote_web_repo_name") repo_dir=\"\$repo_parent/\$repo_name\" $chrome_cmd cd \"\$repo_dir\"/$rel_dir_q $cmd " >"$log_file" 2>&1 } run_unit_tests() { local lang cmd dir log_file status for lang in "${languages[@]}"; do cmd="${unit_cmds[$lang]}" dir="${unit_dirs[$lang]}" log_file="$log_dir/unit_${lang}.log" run_logged "unit $lang: $cmd" "$dir" "$cmd" "$log_file" status=$? unit_status[$lang]="$status" if [ "$status" -eq 0 ]; then unit_result[$lang]="PASS" else unit_result[$lang]="FAIL" fi unit_summary[$lang]="$(tail -n 1 "$log_file" | tr '\n' ' ')" done } run_proto_sync() { local log_file status log_file="$log_dir/proto_sync.log" run_logged "proto sync: $proto_cmd" "$proto_dir" "$proto_cmd" "$log_file" status=$? proto_status="$status" if [ "$status" -eq 0 ]; then proto_result="PASS" else proto_result="FAIL" fi proto_summary="$(tail -n 1 "$log_file" | tr '\n' ' ')" } run_cross_tests() { local item server client dir cmd key log_file status pass_count fail_count for item in "${cross_cases[@]}"; do IFS='|' read -r server client dir cmd <<<"$item" key="$server|$client" log_file="$log_dir/cross_${server}_${client}.log" cross_cmds[$key]="(cd ${dir#$repo_root/} && $cmd)" run_logged "cross $server->$client: $cmd" "$dir" "$cmd" "$log_file" status=$? pass_count="$(count_lines '^PASS scenario=' "$log_file")" fail_count="$(count_lines '^FAIL ' "$log_file")" cross_status[$key]="$status" cross_pass_lines[$key]="$pass_count" cross_fail_lines[$key]="$fail_count" if [ "$status" -eq 0 ] && [ "$fail_count" -eq 0 ] && [ "$pass_count" -eq "$expected_cross_pass_lines" ]; then cross_result[$key]="PASS" else cross_result[$key]="FAIL" fi done } set_web_result() { local key="$1" local status="$2" local log_file="$3" local pass_count fail_count ws_pass_count wss_pass_count pass_count="$(count_lines '^PASS scenario=' "$log_file")" ws_pass_count="$(count_lines '^PASS scenario=ws-' "$log_file")" wss_pass_count="$(count_lines '^PASS scenario=wss-' "$log_file")" fail_count="$(count_lines '^FAIL ' "$log_file")" web_status[$key]="$status" web_pass_lines[$key]="$pass_count" web_ws_pass_lines[$key]="$ws_pass_count" web_wss_pass_lines[$key]="$wss_pass_count" web_fail_lines[$key]="$fail_count" if [ "$status" -eq 0 ] && [ "$fail_count" -eq 0 ] && [ "$pass_count" -eq "$expected_web_pass_lines" ]; then web_result[$key]="PASS" else web_result[$key]="FAIL" fi if [ "$status" -eq 0 ] && [ "$fail_count" -eq 0 ] && [ "$ws_pass_count" -eq "$expected_web_transport_pass_lines" ]; then web_ws_result[$key]="PASS" else web_ws_result[$key]="FAIL" fi if [ "$status" -eq 0 ] && [ "$fail_count" -eq 0 ] && [ "$wss_pass_count" -eq "$expected_web_transport_pass_lines" ]; then web_wss_result[$key]="PASS" else web_wss_result[$key]="FAIL" fi } run_web_tests() { local item server dir cmd key log_file status remote_reason if ! chrome_available; then if ! prepare_remote_web_repo >"$log_dir/remote_web_prepare.log" 2>&1; then remote_reason="$(tail -n 20 "$log_dir/remote_web_prepare.log" | tr '\n' ' ')" for item in "${web_cases[@]}"; do IFS='|' read -r server dir cmd <<<"$item" key="$server" web_cmds[$key]="ssh $remote_web_host '(cd $remote_web_root_expr/$remote_web_repo_name/${dir#$repo_root/} && $cmd)'" web_status[$key]="127" web_pass_lines[$key]="0" web_ws_pass_lines[$key]="0" web_wss_pass_lines[$key]="0" web_fail_lines[$key]="0" web_result[$key]="BLOCKED" web_ws_result[$key]="BLOCKED" web_wss_result[$key]="BLOCKED" web_block_reason[$key]="Remote Dart.web environment unavailable: $remote_reason" printf 'BLOCKED remote web %s->Dart.web: %s\n' "$server" "$remote_reason" >&2 done return fi remote_web_execution="remote" remote_web_prepare_summary="$(grep '^remote Dart.web ' "$log_dir/remote_web_prepare.log" 2>/dev/null | tr '\n' ' ')" if ! sync_remote_web_worktree >"$log_dir/remote_web_sync.log" 2>&1; then remote_reason="$(tail -n 20 "$log_dir/remote_web_sync.log" | tr '\n' ' ')" for item in "${web_cases[@]}"; do IFS='|' read -r server dir cmd <<<"$item" key="$server" web_cmds[$key]="ssh $remote_web_host '(cd $remote_web_root_expr/$remote_web_repo_name/${dir#$repo_root/} && $cmd)'" web_status[$key]="127" web_pass_lines[$key]="0" web_ws_pass_lines[$key]="0" web_wss_pass_lines[$key]="0" web_fail_lines[$key]="0" web_result[$key]="BLOCKED" web_ws_result[$key]="BLOCKED" web_wss_result[$key]="BLOCKED" web_block_reason[$key]="Remote Dart.web worktree sync failed: $remote_reason" printf 'BLOCKED remote web %s->Dart.web: %s\n' "$server" "$remote_reason" >&2 done return fi for item in "${web_cases[@]}"; do IFS='|' read -r server dir cmd <<<"$item" key="$server" log_file="$log_dir/web_${server}.log" web_cmds[$key]="ssh $remote_web_host '(cd $remote_web_root_expr/$remote_web_repo_name/${dir#$repo_root/} && $cmd)'" run_remote_web_logged "$server" "$dir" "$cmd" "$log_file" status=$? set_web_result "$key" "$status" "$log_file" done return fi for item in "${web_cases[@]}"; do IFS='|' read -r server dir cmd <<<"$item" key="$server" log_file="$log_dir/web_${server}.log" web_cmds[$key]="(cd ${dir#$repo_root/} && $cmd)" run_logged "web $server->Dart.web: $cmd" "$dir" "$cmd" "$log_file" status=$? set_web_result "$key" "$status" "$log_file" done } print_proto_table() { printf '\n**Proto 동기화**\n' printf '| 검사 | 명령 | 결과 |\n' printf '|---|---|---|\n' printf '| schema sync | `%s` | %s |\n' "$proto_cmd" "$proto_result" } print_unit_table() { local lang printf '\n**동일언어**\n' printf '| 언어 | 명령 | 결과 |\n' printf '|---|---|---|\n' for lang in "${languages[@]}"; do printf '| %s | `%s` | %s |\n' "$lang" "${unit_cmds[$lang]}" "${unit_result[$lang]:--}" done } language_key() { local label="$1" if [ "$label" = "Dart.io" ]; then echo "Dart" return fi echo "$label" } display_language_label() { local label="$1" if [ "$label" = "Dart" ]; then echo "Dart.io" return fi echo "$label" } language_matrix_cell() { local server_label="$1" local client_label="$2" local server client key if [ "$client_label" = "Dart.web" ]; then echo "${web_ws_result[$server_label]:--}" return fi if [ "$client_label" = "Dart.web(WSS)" ]; then echo "${web_wss_result[$server_label]:--}" return fi server="$(language_key "$server_label")" client="$(language_key "$client_label")" key="$server|$client" if [ "$server" = "$client" ]; then if [ "$mode" = "all" ] || [ "$mode" = "unit" ]; then echo "${unit_result[$server]:--}" else echo "-" fi return fi echo "${cross_result[$key]:--}" } print_language_pass_matrix() { local server client local servers=("Dart.io" "Go" "Kotlin" "Python" "TypeScript") local clients=("Dart.io" "Dart.web" "Dart.web(WSS)" "Go" "Kotlin" "Python" "TypeScript") printf '\n**언어 PASS 매트릭스**\n' printf '| 서버 \\ 클라이언트' for client in "${clients[@]}"; do printf ' | %s' "$client" done printf ' |\n' printf '|---' for client in "${clients[@]}"; do printf '|---' done printf '|\n' for server in "${servers[@]}"; do printf '| %s' "$server" for client in "${clients[@]}"; do printf ' | %s' "$(language_matrix_cell "$server" "$client")" done printf ' |\n' done } print_cross_detail_table() { local item server client dir cmd key printf '\n**크로스테스트 상세**\n' printf '| 방향 | 결과 | PASS scenarios | Expected | FAIL lines |\n' printf '|---|---:|---:|---:|---:|\n' for item in "${cross_cases[@]}"; do IFS='|' read -r server client dir cmd <<<"$item" key="$server|$client" printf '| %s -> %s | %s | %s | %s | %s |\n' \ "$(display_language_label "$server")" "$(display_language_label "$client")" \ "${cross_result[$key]:--}" "${cross_pass_lines[$key]:--}" "$expected_cross_pass_lines" "${cross_fail_lines[$key]:--}" done for item in "${web_cases[@]}"; do IFS='|' read -r server dir cmd <<<"$item" key="$server" printf '| %s -> Dart.web | %s | %s | %s | %s |\n' \ "$server" "${web_ws_result[$key]:--}" "${web_ws_pass_lines[$key]:--}" "$expected_web_transport_pass_lines" "${web_fail_lines[$key]:--}" printf '| %s -> Dart.web(WSS) | %s | %s | %s | %s |\n' \ "$server" "${web_wss_result[$key]:--}" "${web_wss_pass_lines[$key]:--}" "$expected_web_transport_pass_lines" "${web_fail_lines[$key]:--}" done } print_failures() { local failed=0 local lang item server client dir cmd key log_file if [ "${proto_result:-}" = "FAIL" ]; then failed=1 log_file="$log_dir/proto_sync.log" printf '\n**실패: Proto 동기화 검사**\n' printf '재현 명령: `(cd . && %s)`\n\n' "$proto_cmd" tail -n 80 "$log_file" printf '\n' fi for lang in "${languages[@]}"; do if [ "${unit_result[$lang]:-}" = "FAIL" ]; then failed=1 log_file="$log_dir/unit_${lang}.log" printf '\n**실패: %s 유닛/동일언어 테스트**\n' "$lang" printf '재현 명령: `(cd %s && %s)`\n\n' "${unit_dirs[$lang]#$repo_root/}" "${unit_cmds[$lang]}" tail -n 80 "$log_file" printf '\n' fi done for item in "${cross_cases[@]}"; do IFS='|' read -r server client dir cmd <<<"$item" key="$server|$client" if [ "${cross_result[$key]:-}" = "FAIL" ]; then failed=1 log_file="$log_dir/cross_${server}_${client}.log" printf '\n**실패: %s -> %s 크로스테스트**\n' "$server" "$client" printf '재현 명령: `%s`\n\n' "${cross_cmds[$key]}" tail -n 80 "$log_file" printf '\n' fi done for item in "${web_cases[@]}"; do IFS='|' read -r server dir cmd <<<"$item" key="$server" if [ "${web_result[$key]:-}" = "BLOCKED" ]; then failed=1 printf '\n**차단: %s -> Dart.web 크로스테스트**\n' "$server" printf '재현 명령: `%s`\n' "${web_cmds[$key]}" printf '차단 사유: %s\n\n' "${web_block_reason[$key]:-unknown}" elif [ "${web_result[$key]:-}" = "FAIL" ]; then failed=1 log_file="$log_dir/web_${server}.log" printf '\n**실패: %s -> Dart.web 크로스테스트**\n' "$server" printf '재현 명령: `%s`\n\n' "${web_cmds[$key]}" tail -n 80 "$log_file" printf '\n' fi done return "$failed" } has_blocked_results() { local item server dir cmd key for item in "${web_cases[@]}"; do IFS='|' read -r server dir cmd <<<"$item" key="$server" if [ "${web_result[$key]:-}" = "BLOCKED" ]; then return 0 fi done return 1 } has_fail_results() { local lang item server client dir cmd key if [ "${proto_result:-}" = "FAIL" ]; then return 0 fi for lang in "${languages[@]}"; do if [ "${unit_result[$lang]:-}" = "FAIL" ]; then return 0 fi done for item in "${cross_cases[@]}"; do IFS='|' read -r server client dir cmd <<<"$item" key="$server|$client" if [ "${cross_result[$key]:-}" = "FAIL" ]; then return 0 fi done for item in "${web_cases[@]}"; do IFS='|' read -r server dir cmd <<<"$item" key="$server" if [ "${web_result[$key]:-}" = "FAIL" ]; then return 0 fi done return 1 } overall_result_value() { if has_fail_results; then echo "FAIL" elif has_blocked_results; then echo "BLOCKED" else echo "PASS" fi } print_report_body() { if [ "$mode" = "all" ] || [ "$mode" = "proto" ]; then print_proto_table fi if [ "$mode" = "all" ] || [ "$mode" = "unit" ]; then print_unit_table fi if [ "$mode" = "all" ] || [ "$mode" = "cross" ]; then print_language_pass_matrix print_cross_detail_table fi printf '\n로그 디렉터리: `%s`\n' "$log_dir" if print_failures; then return 0 fi return 1 } write_result_record() { local report_body="$1" local exit_code="$2" local overall_result record_dir record_file git_ref overall_result="$(overall_result_value)" record_dir="$repo_root/agent-test/runs" mkdir -p "$record_dir" record_file="$record_dir/${run_record_stamp}-${record_profile}.md" git_ref="$(git -C "$repo_root" rev-parse --short HEAD 2>/dev/null || echo "unknown")" { printf '%s\n' '---' printf 'test_env: local\n' printf 'record_type: test-result\n' printf 'test_profile: %s\n' "$record_profile" printf 'created_at: %s\n' "$run_started_at" printf 'overall_result: %s\n' "$overall_result" printf '%s\n\n' '---' printf '# %s local 결과 기록\n\n' "$record_profile" printf '## 실행 정보\n\n' printf -- '- 실행 일시: %s\n' "$run_started_at" printf -- '- git ref: %s\n' "$git_ref" printf -- '- 실행 명령: `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --%s`\n' "$mode" printf -- '- exit code: %s\n' "$exit_code" printf -- '- 로그 디렉터리: `%s`\n' "$log_dir" if [ "$mode" = "all" ] || [ "$mode" = "cross" ]; then printf -- '- Dart.web 실행 환경: %s\n' "$remote_web_execution" if [ "$remote_web_execution" = "remote" ]; then printf -- '- Dart.web remote host: `%s`\n' "$remote_web_host" printf -- '- Dart.web remote path: `%s/%s`\n' "$remote_web_root_expr" "$remote_web_repo_name" printf -- '- Dart.web remote 준비: %s\n' "$remote_web_prepare_summary" fi fi printf -- '- 전체 결과값: %s\n\n' "$overall_result" printf '## git status 요약\n\n' printf '```text\n' git -C "$repo_root" status --short 2>/dev/null || true printf '```\n\n' cat "$report_body" } >"$record_file" printf '%s' "$record_file" } main() { local report_body exit_code record_file display_record_file case "$mode" in all) run_proto_sync run_unit_tests run_cross_tests run_web_tests ;; unit) run_unit_tests ;; cross) run_cross_tests run_web_tests ;; proto) run_proto_sync ;; esac report_body="$log_dir/report.md" if print_report_body >"$report_body"; then exit_code=0 else exit_code=1 fi record_file="$(write_result_record "$report_body" "$exit_code")" cat "$report_body" display_record_file="${record_file#$repo_root/}" printf '\n결과 기록 파일: `%s`\n' "$display_record_file" exit "$exit_code" } main "$@"