blob: 223199d78ed74ef84950c224aba9dfe6793ad4ab [file]
#!/bin/bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
set -euo pipefail
# Unified script to run SDK examples from README.md files.
# Usage: ./scripts/run-examples-from-readme.sh [OPTIONS]
#
# --language LANG Language to test: rust|go|node|python|php|java|csharp (default: all)
# --target TARGET Cargo target architecture for the server binary
# --skip-tls Skip TLS example tests
#
# The script sources shared utilities from scripts/utils.sh, starts the iggy
# server (built from source), parses example commands from each language's
# README.md, and executes them. Non-TLS examples run first; then the server
# is restarted with TLS for TLS-specific examples.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=scripts/utils.sh
source "${SCRIPT_DIR}/utils.sh"
ROOT_WORKDIR="$(pwd)"
# ---------------------------------------------------------------------------
# Argument parsing
# ---------------------------------------------------------------------------
LANGUAGE="all"
TARGET=""
SKIP_TLS=false
while [[ $# -gt 0 ]]; do
case "$1" in
--language) LANGUAGE="$2"; shift 2 ;;
--target) TARGET="$2"; shift 2 ;;
--skip-tls) SKIP_TLS=true; shift ;;
*)
echo "Unknown option: $1"
echo "Usage: $0 [--language LANG] [--target TARGET] [--skip-tls]"
exit 1
;;
esac
done
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
# Run a full non-TLS + TLS cycle for one language.
# Arguments:
# $1 - language label (for logging)
# $2 - working directory (relative to repo root, or "." for root)
# $3 - space-separated list of README files (relative to workdir)
# $4 - grep pattern for non-TLS commands
# $5 - grep exclude pattern for non-TLS commands (empty = no exclude)
# $6 - grep pattern for TLS commands (empty = no TLS examples)
# $7 - per-command timeout in seconds (0 = no timeout)
# $8 - extra server args (e.g. "--fresh")
# $9 - optional pre-flight callback function name (run before non-TLS examples, with server already started)
# shellcheck disable=SC2329
run_language_examples() {
local lang="$1"
local workdir="$2"
local readme_files="$3"
local grep_pattern="$4"
local grep_exclude="$5"
local tls_grep_pattern="$6"
local cmd_timeout="$7"
local server_extra_args="$8"
local preflight_fn="${9:-}"
echo ""
echo "============================================================"
echo " Running ${lang} examples"
echo "============================================================"
echo ""
EXAMPLES_EXIT_CODE=0
# --- Non-TLS pass ---
cleanup_server_state
# shellcheck disable=SC2086
start_plain_server ${server_extra_args}
wait_for_server_ready "${lang}"
# Run optional pre-flight callback (e.g. CLI commands from root README)
if [ -n "${preflight_fn}" ] && declare -f "${preflight_fn}" >/dev/null 2>&1; then
${preflight_fn}
if [ "${EXAMPLES_EXIT_CODE}" -ne 0 ]; then
cd "${ROOT_WORKDIR}"
stop_server
report_result "${EXAMPLES_EXIT_CODE}"
return "${EXAMPLES_EXIT_CODE}"
fi
fi
if [ "${workdir}" != "." ]; then
cd "${workdir}"
fi
for readme in ${readme_files}; do
if [ "${EXAMPLES_EXIT_CODE}" -ne 0 ]; then
break
fi
run_readme_commands "${readme}" "${grep_pattern}" "${cmd_timeout}" "${grep_exclude}"
done
cd "${ROOT_WORKDIR}"
stop_server
# --- TLS pass ---
if [ "${EXAMPLES_EXIT_CODE}" -eq 0 ] && [ "${SKIP_TLS}" = false ] && [ -n "${tls_grep_pattern}" ]; then
local has_tls=false
for readme in ${readme_files}; do
local readme_path="${readme}"
if [ "${workdir}" != "." ]; then
readme_path="${workdir}/${readme}"
fi
if [ -f "${readme_path}" ] && grep -qE "${tls_grep_pattern}" "${readme_path}" 2>/dev/null; then
has_tls=true
break
fi
done
if [ "${has_tls}" = true ]; then
echo ""
echo "=== Running ${lang} TLS examples ==="
echo ""
cleanup_server_state
# shellcheck disable=SC2086
start_tls_server ${server_extra_args}
wait_for_server_ready "${lang} TLS"
if [ "${workdir}" != "." ]; then
cd "${workdir}"
fi
for readme in ${readme_files}; do
if [ "${EXAMPLES_EXIT_CODE}" -ne 0 ]; then
break
fi
run_readme_commands "${readme}" "${tls_grep_pattern}" "${cmd_timeout}"
done
cd "${ROOT_WORKDIR}"
stop_server
fi
fi
report_result "${EXAMPLES_EXIT_CODE}"
return "${EXAMPLES_EXIT_CODE}"
}
# ---------------------------------------------------------------------------
# Per-language runners
# ---------------------------------------------------------------------------
# shellcheck disable=SC2329
run_rust_examples() {
resolve_server_binary "${TARGET}"
resolve_cli_binary "${TARGET}"
if [ -n "${TARGET}" ]; then
TRANSFORM_COMMAND() {
echo "$1" | sed "s|cargo r |cargo r --target ${TARGET} |g" | sed "s|cargo run |cargo run --target ${TARGET} |g"
}
else
unset -f TRANSFORM_COMMAND 2>/dev/null || true
fi
# Pre-flight: run CLI commands from root README
_rust_preflight() {
run_readme_commands "README.md" '^\`cargo r --bin iggy -- '
}
run_language_examples \
"Rust" \
"." \
"README.md examples/rust/README.md" \
"^cargo run --example" \
"tcp-tls" \
"^cargo run --example.*tcp-tls" \
0 \
"" \
"_rust_preflight"
}
# shellcheck disable=SC2329
run_node_examples() {
resolve_server_binary "${TARGET}"
export DEBUG=iggy:examples
unset -f TRANSFORM_COMMAND 2>/dev/null || true
run_language_examples \
"Node.js" \
"examples/node" \
"README.md" \
"^(npm run|tsx)" \
"tcp-tls" \
"^(npm run|tsx).*tcp-tls" \
0 \
""
}
# shellcheck disable=SC2329
run_go_examples() {
resolve_server_binary "${TARGET}"
unset -f TRANSFORM_COMMAND 2>/dev/null || true
run_language_examples \
"Go" \
"examples/go" \
"README.md" \
"^go run" \
"--tls" \
"^go run.*--tls" \
0 \
""
}
# shellcheck disable=SC2329
run_python_examples() {
resolve_server_binary "${TARGET}"
unset -f TRANSFORM_COMMAND 2>/dev/null || true
echo ""
echo "============================================================"
echo " Running Python examples"
echo "============================================================"
echo ""
EXAMPLES_EXIT_CODE=0
# --- Non-TLS pass ---
cleanup_server_state
start_plain_server --fresh
wait_for_server_ready "Python"
cd examples/python || exit 1
echo "Syncing Python dependencies with uv..."
uv sync --frozen
run_readme_commands "README.md" "^uv run " 10 "--tls"
cd "${ROOT_WORKDIR}"
stop_server
# --- TLS pass ---
if [ "${EXAMPLES_EXIT_CODE}" -eq 0 ] && [ "${SKIP_TLS}" = false ]; then
local tls_readme="examples/python/README.md"
if [ -f "${tls_readme}" ] && grep -qE "^uv run.*tls" "${tls_readme}" 2>/dev/null; then
echo ""
echo "=== Running Python TLS examples ==="
echo ""
cleanup_server_state
start_tls_server --fresh
wait_for_server_ready "Python TLS"
cd examples/python || exit 1
run_readme_commands "README.md" "^uv run.*tls" 10
cd "${ROOT_WORKDIR}"
stop_server
fi
fi
report_result "${EXAMPLES_EXIT_CODE}"
return "${EXAMPLES_EXIT_CODE}"
}
# shellcheck disable=SC2329
run_php_examples() {
resolve_server_binary "${TARGET}"
local php_bin="${PHP:-php}"
if [ -z "${PHP_IGGY_EXTENSION:-}" ]; then
local extension_candidate
for extension_candidate in \
target/debug/libiggy_php.so \
target/debug/libiggy_php.dylib \
foreign/php/target/debug/libiggy_php.so \
foreign/php/target/debug/libiggy_php.dylib; do
if [ -f "${extension_candidate}" ]; then
PHP_IGGY_EXTENSION="$(pwd)/${extension_candidate}"
break
fi
done
export PHP_IGGY_EXTENSION
fi
if [ -z "${PHP_IGGY_EXTENSION:-}" ] || [ ! -f "${PHP_IGGY_EXTENSION}" ]; then
echo "Error: PHP extension not found. Build foreign/php first or set PHP_IGGY_EXTENSION."
exit 1
fi
TRANSFORM_COMMAND() {
local command="$1"
printf '%s\n' "${command/#php /\"${php_bin}\" }"
}
run_language_examples \
"PHP" \
"examples/php" \
"README.md" \
"^php " \
"" \
"" \
0 \
"--fresh"
}
# shellcheck disable=SC2329
run_java_examples() {
resolve_server_binary "${TARGET}"
unset -f TRANSFORM_COMMAND 2>/dev/null || true
run_language_examples \
"Java" \
"examples/java" \
"README.md" \
'^\./gradlew' \
"TcpTls" \
'^\./gradlew.*TcpTls' \
0 \
""
}
# shellcheck disable=SC2329
run_csharp_examples() {
resolve_server_binary "${TARGET}"
unset -f TRANSFORM_COMMAND 2>/dev/null || true
# Pre-flight: run CLI commands from root README
_csharp_preflight() {
run_readme_commands "README.md" '^\`cargo r --bin iggy -- '
}
run_language_examples \
"C#" \
"." \
"README.md examples/csharp/README.md" \
"^dotnet run --project" \
"TcpTls" \
"^dotnet run --project.*TcpTls" \
0 \
"" \
"_csharp_preflight"
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
overall_exit_code=0
run_one() {
local lang_fn="$1"
local lang_name="$2"
EXAMPLES_EXIT_CODE=0
unset -f TRANSFORM_COMMAND 2>/dev/null || true
set +e
${lang_fn}
local rc=$?
set -e
unset -f TRANSFORM_COMMAND 2>/dev/null || true
if [ ${rc} -ne 0 ]; then
echo ""
echo -e "\e[31m${lang_name} examples FAILED (exit code ${rc})\e[0m"
overall_exit_code=1
fi
cd "${ROOT_WORKDIR}"
}
case "${LANGUAGE}" in
rust) run_one run_rust_examples "Rust" ;;
node) run_one run_node_examples "Node" ;;
go) run_one run_go_examples "Go" ;;
python) run_one run_python_examples "Python" ;;
php) run_one run_php_examples "PHP" ;;
java) run_one run_java_examples "Java" ;;
csharp) run_one run_csharp_examples "C#" ;;
all)
for lang in rust node go python php java csharp; do
run_one "run_${lang}_examples" "${lang}"
done
;;
*)
echo "Unknown language: ${LANGUAGE}"
echo "Supported: rust, node, go, python, php, java, csharp, all"
exit 1
;;
esac
exit "${overall_exit_code}"