Skip to content

Commit e1141ee

Browse files
committed
fix: fix nim interop env setup file
1 parent 8e74f94 commit e1141ee

File tree

5 files changed

+218
-148
lines changed

5 files changed

+218
-148
lines changed

.github/workflows/tox.yml

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,34 +36,48 @@ jobs:
3636
- uses: actions/setup-python@v5
3737
with:
3838
python-version: ${{ matrix.python }}
39-
- run: |
40-
python -m pip install --upgrade pip
41-
python -m pip install tox
42-
- run: |
43-
python -m tox run -r
4439

45-
windows:
46-
runs-on: windows-latest
47-
strategy:
48-
matrix:
49-
python-version: ["3.11", "3.12", "3.13"]
50-
toxenv: [core, wheel]
51-
fail-fast: false
52-
steps:
53-
- uses: actions/checkout@v4
54-
- name: Set up Python ${{ matrix.python-version }}
55-
uses: actions/setup-python@v5
40+
# Add Nim installation for interop tests
41+
- name: Install Nim for interop testing
42+
if: matrix.toxenv == 'interop'
43+
run: |
44+
echo "Installing Nim for nim-libp2p interop testing..."
45+
curl -sSf https://nim-lang.org/choosenim/init.sh | sh -s -- -y --firstInstall
46+
echo "$HOME/.nimble/bin" >> $GITHUB_PATH
47+
echo "$HOME/.choosenim/toolchains/nim-stable/bin" >> $GITHUB_PATH
48+
49+
# Cache nimble packages - ADD THIS
50+
- name: Cache nimble packages
51+
if: matrix.toxenv == 'interop'
52+
uses: actions/cache@v4
5653
with:
57-
python-version: ${{ matrix.python-version }}
58-
- name: Install dependencies
54+
path: |
55+
~/.nimble
56+
~/.choosenim/toolchains/*/lib
57+
key: ${{ runner.os }}-nimble-${{ hashFiles('**/nim_echo_server.nim') }}
58+
restore-keys: |
59+
${{ runner.os }}-nimble-
60+
61+
- name: Build nim interop binaries
62+
if: matrix.toxenv == 'interop'
5963
run: |
64+
export PATH="$HOME/.nimble/bin:$HOME/.choosenim/toolchains/nim-stable/bin:$PATH"
65+
cd tests/interop/nim_libp2p
66+
./scripts/setup_nim_echo.sh
67+
68+
- run: |
6069
python -m pip install --upgrade pip
6170
python -m pip install tox
62-
- name: Test with tox
63-
shell: bash
71+
72+
- name: Run Tests or Generate Docs
6473
run: |
65-
if [[ "${{ matrix.toxenv }}" == "wheel" ]]; then
66-
python -m tox run -e windows-wheel
74+
if [[ "${{ matrix.toxenv }}" == 'docs' ]]; then
75+
export TOXENV=docs
6776
else
68-
python -m tox run -e py311-${{ matrix.toxenv }}
77+
export TOXENV=py${{ matrix.python }}-${{ matrix.toxenv }}
6978
fi
79+
# Set PATH for nim commands during tox
80+
if [[ "${{ matrix.toxenv }}" == 'interop' ]]; then
81+
export PATH="$HOME/.nimble/bin:$HOME/.choosenim/toolchains/nim-stable/bin:$PATH"
82+
fi
83+
python -m tox run -r

pyproject.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ dev = [
7878
"pytest>=7.0.0",
7979
"pytest-xdist>=2.4.0",
8080
"pytest-trio>=0.5.2",
81+
"pytest-timeout>=2.4.0",
8182
"factory-boy>=2.12.0,<3.0.0",
8283
"ruff>=0.11.10",
8384
"pyrefly (>=0.17.1,<0.18.0)",
@@ -89,11 +90,12 @@ docs = [
8990
"tomli; python_version < '3.11'",
9091
]
9192
test = [
93+
"factory-boy>=2.12.0,<3.0.0",
9294
"p2pclient==0.2.0",
9395
"pytest>=7.0.0",
94-
"pytest-xdist>=2.4.0",
96+
"pytest-timeout>=2.4.0",
9597
"pytest-trio>=0.5.2",
96-
"factory-boy>=2.12.0,<3.0.0",
98+
"pytest-xdist>=2.4.0",
9799
]
98100

99101
[tool.setuptools]

tests/interop/nim_libp2p/conftest.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import fcntl
2+
import logging
3+
from pathlib import Path
4+
import shutil
5+
import subprocess
6+
import time
7+
8+
import pytest
9+
10+
logger = logging.getLogger(__name__)
11+
12+
13+
def check_nim_available():
14+
"""Check if nim compiler is available."""
15+
return shutil.which("nim") is not None and shutil.which("nimble") is not None
16+
17+
18+
def check_nim_binary_built():
19+
"""Check if nim echo server binary is built."""
20+
current_dir = Path(__file__).parent
21+
binary_path = current_dir / "nim_echo_server"
22+
return binary_path.exists() and binary_path.stat().st_size > 0
23+
24+
25+
def run_nim_setup_with_lock():
26+
"""Run nim setup with file locking to prevent parallel execution."""
27+
current_dir = Path(__file__).parent
28+
lock_file = current_dir / ".setup_lock"
29+
setup_script = current_dir / "scripts" / "setup_nim_echo.sh"
30+
31+
if not setup_script.exists():
32+
raise RuntimeError(f"Setup script not found: {setup_script}")
33+
34+
# Try to acquire lock
35+
try:
36+
with open(lock_file, "w") as f:
37+
# Non-blocking lock attempt
38+
fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
39+
40+
# Double-check binary doesn't exist (another worker might have built it)
41+
if check_nim_binary_built():
42+
logger.info("Binary already exists, skipping setup")
43+
return
44+
45+
logger.info("Acquired setup lock, running nim-libp2p setup...")
46+
47+
# Make setup script executable and run it
48+
setup_script.chmod(0o755)
49+
result = subprocess.run(
50+
[str(setup_script)],
51+
cwd=current_dir,
52+
capture_output=True,
53+
text=True,
54+
timeout=300, # 5 minute timeout
55+
)
56+
57+
if result.returncode != 0:
58+
raise RuntimeError(
59+
f"Setup failed (exit {result.returncode}):\n"
60+
f"stdout: {result.stdout}\n"
61+
f"stderr: {result.stderr}"
62+
)
63+
64+
# Verify binary was built
65+
if not check_nim_binary_built():
66+
raise RuntimeError("nim_echo_server binary not found after setup")
67+
68+
logger.info("nim-libp2p setup completed successfully")
69+
70+
except BlockingIOError:
71+
# Another worker is running setup, wait for it to complete
72+
logger.info("Another worker is running setup, waiting...")
73+
74+
# Wait for setup to complete (check every 2 seconds, max 5 minutes)
75+
for _ in range(150): # 150 * 2 = 300 seconds = 5 minutes
76+
if check_nim_binary_built():
77+
logger.info("Setup completed by another worker")
78+
return
79+
time.sleep(2)
80+
81+
raise TimeoutError("Timed out waiting for setup to complete")
82+
83+
finally:
84+
# Clean up lock file
85+
try:
86+
lock_file.unlink(missing_ok=True)
87+
except Exception:
88+
pass
89+
90+
91+
@pytest.fixture(scope="function") # Changed to function scope
92+
def nim_echo_binary():
93+
"""Get nim echo server binary path."""
94+
current_dir = Path(__file__).parent
95+
binary_path = current_dir / "nim_echo_server"
96+
97+
if not binary_path.exists():
98+
pytest.skip(
99+
"nim_echo_server binary not found. "
100+
"Run setup script: ./scripts/setup_nim_echo.sh"
101+
)
102+
103+
return binary_path
104+
105+
106+
@pytest.fixture
107+
async def nim_server(nim_echo_binary):
108+
"""Start and stop nim echo server for tests."""
109+
# Import here to avoid circular imports
110+
# pyrefly: ignore
111+
from test_echo_interop import NimEchoServer
112+
113+
server = NimEchoServer(nim_echo_binary)
114+
115+
try:
116+
peer_id, listen_addr = await server.start()
117+
yield server, peer_id, listen_addr
118+
finally:
119+
await server.stop()
Lines changed: 42 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
#!/usr/bin/env bash
2-
# Simple setup script for nim echo server interop testing
2+
# tests/interop/nim_libp2p/scripts/setup_nim_echo.sh
3+
# Cache-aware setup that skips installation if packages exist
34

45
set -euo pipefail
56

7+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8+
PROJECT_DIR="${SCRIPT_DIR}/.."
9+
610
# Colors
711
GREEN='\033[0;32m'
812
RED='\033[0;31m'
@@ -13,86 +17,58 @@ log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
1317
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
1418
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
1519

16-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17-
PROJECT_ROOT="${SCRIPT_DIR}/.."
18-
NIM_LIBP2P_DIR="${PROJECT_ROOT}/nim-libp2p"
20+
main() {
21+
log_info "Setting up nim echo server for interop testing..."
1922

20-
# Check prerequisites
21-
check_nim() {
22-
if ! command -v nim &> /dev/null; then
23-
log_error "Nim not found. Install with: curl -sSf https://nim-lang.org/choosenim/init.sh | sh"
24-
exit 1
25-
fi
26-
if ! command -v nimble &> /dev/null; then
27-
log_error "Nimble not found. Please install Nim properly."
23+
# Check if nim is available
24+
if ! command -v nim &> /dev/null || ! command -v nimble &> /dev/null; then
25+
log_error "Nim not found. Please install nim first."
2826
exit 1
2927
fi
30-
}
3128

32-
# Setup nim-libp2p dependency
33-
setup_nim_libp2p() {
34-
log_info "Setting up nim-libp2p dependency..."
29+
cd "${PROJECT_DIR}"
30+
31+
# Create logs directory
32+
mkdir -p logs
3533

36-
if [ ! -d "${NIM_LIBP2P_DIR}" ]; then
37-
log_info "Cloning nim-libp2p..."
38-
git clone https://github.com/status-im/nim-libp2p.git "${NIM_LIBP2P_DIR}"
34+
# Check if binary already exists
35+
if [[ -f "nim_echo_server" ]]; then
36+
log_info "nim_echo_server already exists, skipping build"
37+
return 0
3938
fi
4039

41-
cd "${NIM_LIBP2P_DIR}"
42-
log_info "Installing nim-libp2p dependencies..."
43-
nimble install -y --depsOnly
44-
}
40+
# Check if libp2p is already installed (cache-aware)
41+
if nimble list -i | grep -q "libp2p"; then
42+
log_info "libp2p already installed, skipping installation"
43+
else
44+
log_info "Installing nim-libp2p globally..."
45+
nimble install -y libp2p
46+
fi
4547

46-
# Build nim echo server
47-
build_echo_server() {
4848
log_info "Building nim echo server..."
49-
50-
cd "${PROJECT_ROOT}"
51-
52-
# Create nimble file if it doesn't exist
53-
cat > nim_echo_test.nimble << 'EOF'
54-
# Package
55-
version = "0.1.0"
56-
author = "py-libp2p interop"
57-
description = "nim echo server for interop testing"
58-
license = "MIT"
59-
60-
# Dependencies
61-
requires "nim >= 1.6.0"
62-
requires "libp2p"
63-
requires "chronos"
64-
requires "stew"
65-
66-
# Binary
67-
bin = @["nim_echo_server"]
68-
EOF
69-
70-
# Build the server
71-
log_info "Compiling nim echo server..."
72-
nim c -d:release -d:chronicles_log_level=INFO -d:libp2p_quic_support --opt:speed --gc:orc -o:nim_echo_server nim_echo_server.nim
73-
74-
if [ -f "nim_echo_server" ]; then
49+
# Compile the echo server
50+
nim c \
51+
-d:release \
52+
-d:chronicles_log_level=INFO \
53+
-d:libp2p_quic_support \
54+
-d:chronos_event_loop=iocp \
55+
-d:ssl \
56+
--opt:speed \
57+
--mm:orc \
58+
--verbosity:1 \
59+
-o:nim_echo_server \
60+
nim_echo_server.nim
61+
62+
# Verify binary was created
63+
if [[ -f "nim_echo_server" ]]; then
7564
log_info "✅ nim_echo_server built successfully"
65+
log_info "Binary size: $(ls -lh nim_echo_server | awk '{print $5}')"
7666
else
7767
log_error "❌ Failed to build nim_echo_server"
7868
exit 1
7969
fi
80-
}
81-
82-
main() {
83-
log_info "Setting up nim echo server for interop testing..."
84-
85-
# Create logs directory
86-
mkdir -p "${PROJECT_ROOT}/logs"
87-
88-
# Clean up any existing processes
89-
pkill -f "nim_echo_server" || true
90-
91-
check_nim
92-
setup_nim_libp2p
93-
build_echo_server
9470

95-
log_info "🎉 Setup complete! You can now run: python -m pytest test_echo_interop.py -v"
71+
log_info "🎉 Setup complete!"
9672
}
9773

9874
main "$@"

0 commit comments

Comments
 (0)