ci: add service host discovery and strict policy docs validation
Some checks failed
API Endpoint Tests / test-api-endpoints (push) Failing after 5s
CLI Tests / test-cli (push) Failing after 3m17s
Documentation Validation / validate-docs (push) Successful in 8s
Documentation Validation / validate-policies-strict (push) Failing after 3s
Integration Tests / test-service-integration (push) Failing after 1s
Package Tests / Python package - aitbc-agent-sdk (push) Successful in 30s
Package Tests / Python package - aitbc-core (push) Failing after 11s
Package Tests / Python package - aitbc-crypto (push) Failing after 11s
Package Tests / Python package - aitbc-sdk (push) Failing after 12s
Package Tests / JavaScript package - aitbc-sdk-js (push) Successful in 6s
Package Tests / JavaScript package - aitbc-token (push) Failing after 18s
Production Tests / Production Integration Tests (push) Failing after 11s
Python Tests / test-python (push) Failing after 2m54s
Security Scanning / security-scan (push) Failing after 48s

- Add multi-candidate host discovery (localhost, host.docker.internal, gateway) in api-endpoint-tests
- Pass discovered service host via AITBC_API_HOST environment variable to test script
- Update test_api_endpoints.py to use AITBC_API_HOST for all service URLs
- Add validate-policies-strict job to docs-validation workflow for policy Markdown files
- Add job names to package-tests matrix for better CI output clarity
- Add --import
This commit is contained in:
aitbc
2026-04-18 12:28:19 +02:00
parent 23348892b9
commit 3f98f3f7bf
10 changed files with 239 additions and 16 deletions

View File

@@ -43,22 +43,51 @@ jobs:
- name: Wait for services - name: Wait for services
run: | run: |
echo "Waiting for AITBC services..." echo "Waiting for AITBC services..."
gateway_host=$(ip route 2>/dev/null | awk '/default/ {print $3; exit}')
host_candidates=(localhost host.docker.internal)
if [[ -n "$gateway_host" ]]; then
host_candidates+=("$gateway_host")
fi
service_host=""
for candidate in "${host_candidates[@]}"; do
code=$(curl -so /dev/null -w '%{http_code}' "http://$candidate:8000/health" 2>/dev/null) || code=0
if [ "$code" -gt 0 ] && [ "$code" -lt 600 ]; then
service_host="$candidate"
break
fi
code=$(curl -so /dev/null -w '%{http_code}' "http://$candidate:8000/v1/health" 2>/dev/null) || code=0
if [ "$code" -gt 0 ] && [ "$code" -lt 600 ]; then
service_host="$candidate"
break
fi
done
if [[ -z "$service_host" ]]; then
echo "❌ Could not find a reachable API host"
exit 1
fi
echo "$service_host" > /var/lib/aitbc-workspaces/api-tests/service_host
echo "Using service host: $service_host"
for port in 8000 8001 8003 8006; do for port in 8000 8001 8003 8006; do
port_ready=0 port_ready=0
for i in $(seq 1 15); do for i in $(seq 1 15); do
code=$(curl -so /dev/null -w '%{http_code}' "http://localhost:$port/health" 2>/dev/null) || code=0 code=$(curl -so /dev/null -w '%{http_code}' "http://$service_host:$port/health" 2>/dev/null) || code=0
if [ "$code" -gt 0 ] && [ "$code" -lt 600 ]; then if [ "$code" -gt 0 ] && [ "$code" -lt 600 ]; then
echo "✅ Port $port ready (HTTP $code)" echo "✅ Port $port ready (HTTP $code)"
port_ready=1 port_ready=1
break break
fi fi
code=$(curl -so /dev/null -w '%{http_code}' "http://localhost:$port/api/health" 2>/dev/null) || code=0 code=$(curl -so /dev/null -w '%{http_code}' "http://$service_host:$port/api/health" 2>/dev/null) || code=0
if [ "$code" -gt 0 ] && [ "$code" -lt 600 ]; then if [ "$code" -gt 0 ] && [ "$code" -lt 600 ]; then
echo "✅ Port $port ready (HTTP $code)" echo "✅ Port $port ready (HTTP $code)"
port_ready=1 port_ready=1
break break
fi fi
code=$(curl -so /dev/null -w '%{http_code}' "http://localhost:$port/" 2>/dev/null) || code=0 code=$(curl -so /dev/null -w '%{http_code}' "http://$service_host:$port/" 2>/dev/null) || code=0
if [ "$code" -gt 0 ] && [ "$code" -lt 600 ]; then if [ "$code" -gt 0 ] && [ "$code" -lt 600 ]; then
echo "✅ Port $port ready (HTTP $code)" echo "✅ Port $port ready (HTTP $code)"
port_ready=1 port_ready=1
@@ -76,7 +105,8 @@ jobs:
- name: Run API endpoint tests - name: Run API endpoint tests
run: | run: |
cd /var/lib/aitbc-workspaces/api-tests/repo cd /var/lib/aitbc-workspaces/api-tests/repo
venv/bin/python scripts/ci/test_api_endpoints.py service_host=$(cat /var/lib/aitbc-workspaces/api-tests/service_host)
AITBC_API_HOST="$service_host" venv/bin/python scripts/ci/test_api_endpoints.py
echo "✅ API endpoint tests completed" echo "✅ API endpoint tests completed"
- name: Cleanup - name: Cleanup

View File

@@ -101,3 +101,43 @@ jobs:
- name: Cleanup - name: Cleanup
if: always() if: always()
run: rm -rf /var/lib/aitbc-workspaces/docs-validation run: rm -rf /var/lib/aitbc-workspaces/docs-validation
validate-policies-strict:
runs-on: debian
timeout-minutes: 10
steps:
- name: Clone repository
run: |
WORKSPACE="/var/lib/aitbc-workspaces/docs-validation-policies"
rm -rf "$WORKSPACE"
mkdir -p "$WORKSPACE"
cd "$WORKSPACE"
git clone --depth 1 http://gitea.bubuit.net:3000/oib/aitbc.git repo
- name: Install markdownlint
run: |
npm install -g markdownlint-cli
- name: Strict lint policy docs
run: |
cd /var/lib/aitbc-workspaces/docs-validation-policies/repo
# Ensure standard directories exist
mkdir -p /var/lib/aitbc/data /var/lib/aitbc/keystore /etc/aitbc /var/log/aitbc
shopt -s globstar nullglob
mapfile -t targets < <(printf '%s\n' docs/policies/*.md docs/policies/**/*.md | awk '!seen[$0]++')
if [[ ${#targets[@]} -eq 0 ]]; then
echo "❌ No policy Markdown files found"
exit 1
fi
echo "Strict docs scope: ${#targets[@]} policy Markdown files"
markdownlint "${targets[@]}"
echo "✅ Policy docs lint passed"
- name: Cleanup
if: always()
run: rm -rf /var/lib/aitbc-workspaces/docs-validation-policies

View File

@@ -17,6 +17,7 @@ concurrency:
jobs: jobs:
test-python-packages: test-python-packages:
name: Python package - ${{ matrix.package.name }}
runs-on: debian runs-on: debian
timeout-minutes: 15 timeout-minutes: 15
@@ -101,6 +102,7 @@ jobs:
run: rm -rf "/var/lib/aitbc-workspaces/pkg-${{ matrix.package.name }}" run: rm -rf "/var/lib/aitbc-workspaces/pkg-${{ matrix.package.name }}"
test-javascript-packages: test-javascript-packages:
name: JavaScript package - ${{ matrix.package.name }}
runs-on: debian runs-on: debian
timeout-minutes: 15 timeout-minutes: 15

View File

@@ -0,0 +1,125 @@
name: Production Tests
on:
push:
branches: [main, develop]
paths:
- 'tests/production/**'
- 'apps/agent-coordinator/**'
- '.gitea/workflows/production-tests.yml'
pull_request:
branches: [main, develop]
workflow_dispatch:
concurrency:
group: production-tests-${{ github.ref }}
cancel-in-progress: true
jobs:
test-production:
name: Production Integration Tests
runs-on: debian
timeout-minutes: 20
steps:
- name: Clone repository
run: |
WORKSPACE="/var/lib/aitbc-workspaces/production-tests"
rm -rf "$WORKSPACE"
mkdir -p "$WORKSPACE"
cd "$WORKSPACE"
git clone --depth 1 http://gitea.bubuit.net:3000/oib/aitbc.git repo
- name: Setup test environment
run: |
cd /var/lib/aitbc-workspaces/production-tests/repo
python3 -m venv venv
venv/bin/pip install -q \
pytest \
pytest-asyncio \
pytest-timeout \
requests \
pyjwt \
fastapi \
uvicorn[standard] \
redis \
bcrypt \
websockets \
numpy \
psutil \
prometheus-client
# Ensure standard directories exist
mkdir -p /var/lib/aitbc/data /var/lib/aitbc/keystore /etc/aitbc /var/log/aitbc
- name: Start Redis
run: |
redis-server --daemonize yes --port 6379
sleep 2
redis-cli ping || exit 1
echo "✅ Redis started"
- name: Start agent coordinator
run: |
cd /var/lib/aitbc-workspaces/production-tests/repo
source venv/bin/activate
export PYTHONPATH="apps/agent-coordinator/src:$PYTHONPATH"
# Start agent coordinator in background
nohup uvicorn src.app.main:app \
--host 0.0.0.0 \
--port 9001 \
--log-level info \
> /tmp/agent-coordinator.log 2>&1 &
echo $! > /tmp/agent-coordinator.pid
echo "✅ Agent coordinator started (PID: $(cat /tmp/agent-coordinator.pid))"
- name: Wait for agent coordinator ready
run: |
echo "Waiting for agent coordinator on port 9001..."
for i in $(seq 1 30); do
code=$(curl -so /dev/null -w '%{http_code}' "http://localhost:9001/health" 2>/dev/null) || code=0
if [ "$code" -ge 200 ] && [ "$code" -lt 600 ]; then
echo "✅ Agent coordinator ready (HTTP $code)"
exit 0
fi
sleep 2
done
echo "❌ Agent coordinator not ready"
cat /tmp/agent-coordinator.log
exit 1
- name: Run production tests
run: |
cd /var/lib/aitbc-workspaces/production-tests/repo
source venv/bin/activate
export PYTHONPATH="apps/agent-coordinator/src:$PYTHONPATH"
pytest tests/production/ \
-v \
--tb=short \
--timeout=30 \
--import-mode=importlib \
-k "not test_error_handling"
echo "✅ Production tests completed"
- name: Agent coordinator logs
if: always()
run: |
if [ -f /tmp/agent-coordinator.log ]; then
echo "=== Agent Coordinator Logs ==="
cat /tmp/agent-coordinator.log
fi
- name: Cleanup
if: always()
run: |
if [ -f /tmp/agent-coordinator.pid ]; then
kill $(cat /tmp/agent-coordinator.pid) 2>/dev/null || true
rm -f /tmp/agent-coordinator.pid
fi
pkill -f "uvicorn src.app.main:app" 2>/dev/null || true
redis-cli shutdown 2>/dev/null || true
rm -rf /var/lib/aitbc-workspaces/production-tests

View File

@@ -78,7 +78,8 @@ jobs:
apps/wallet/tests/ \ apps/wallet/tests/ \
packages/py/aitbc-crypto/tests/ \ packages/py/aitbc-crypto/tests/ \
packages/py/aitbc-sdk/tests/ \ packages/py/aitbc-sdk/tests/ \
--tb=short -q --timeout=30 \ --tb=short -q --timeout=30 --import-mode=importlib \
--ignore=tests/production \
--ignore=apps/coordinator-api/tests/test_confidential*.py --ignore=apps/coordinator-api/tests/test_confidential*.py
echo "✅ Python tests completed" echo "✅ Python tests completed"

View File

@@ -242,6 +242,11 @@ def success(message: str):
console.print(Panel(f"[green]{message}[/green]", title="")) console.print(Panel(f"[green]{message}[/green]", title=""))
def info(message: str):
"""Print informational message"""
console.print(Panel(f"[cyan]{message}[/cyan]", title=""))
def warning(message: str): def warning(message: str):
"""Print warning message""" """Print warning message"""
console.print(Panel(f"[yellow]{message}[/yellow]", title="⚠️")) console.print(Panel(f"[yellow]{message}[/yellow]", title="⚠️"))

View File

@@ -5,7 +5,8 @@ description = "AITBC Agent SDK"
authors = [ authors = [
{name = "AITBC Team", email = "team@aitbc.dev"} {name = "AITBC Team", email = "team@aitbc.dev"}
] ]
requires-python = "^3.13" readme = "README.md"
requires-python = ">=3.13"
dependencies = [ dependencies = [
"requests>=2.31.0", "requests>=2.31.0",
"pydantic>=2.5.0" "pydantic>=2.5.0"
@@ -14,3 +15,8 @@ dependencies = [
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.poetry]
packages = [
{ include = "aitbc_agent", from = "src" }
]

View File

@@ -5,7 +5,8 @@ description = "AITBC Cryptography"
authors = [ authors = [
{name = "AITBC Team", email = "team@aitbc.dev"} {name = "AITBC Team", email = "team@aitbc.dev"}
] ]
requires-python = "^3.13" readme = "README.md"
requires-python = ">=3.13"
dependencies = [ dependencies = [
"cryptography>=41.0.0" "cryptography>=41.0.0"
] ]
@@ -13,3 +14,8 @@ dependencies = [
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.poetry]
packages = [
{ include = "aitbc_crypto", from = "src" }
]

View File

@@ -6,7 +6,7 @@ authors = [
{name = "AITBC Team", email = "team@aitbc.dev"} {name = "AITBC Team", email = "team@aitbc.dev"}
] ]
readme = "README.md" readme = "README.md"
requires-python = "^3.13" requires-python = ">=3.13"
dependencies = [ dependencies = [
"cryptography>=41.0.0", "cryptography>=41.0.0",
"requests>=2.31.0", "requests>=2.31.0",
@@ -16,3 +16,8 @@ dependencies = [
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.poetry]
packages = [
{ include = "aitbc_sdk", from = "src" }
]

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""CI test script for AITBC API endpoints.""" """CI test script for AITBC API endpoints."""
import os
import requests import requests
import json import json
import time import time
@@ -8,11 +9,13 @@ import statistics
import sys import sys
# Service ports (must match systemd config) # Service ports (must match systemd config)
API_HOST = os.environ.get("AITBC_API_HOST", "localhost")
SERVICES = { SERVICES = {
"coordinator": {"url": "http://localhost:8000", "endpoints": ["/", "/health", "/info"]}, "coordinator": {"url": f"http://{API_HOST}:8000", "endpoints": ["/", "/health", "/info"]},
"exchange": {"url": "http://localhost:8001", "endpoints": ["/", "/api/health", "/health", "/info"]}, "exchange": {"url": f"http://{API_HOST}:8001", "endpoints": ["/", "/api/health", "/health", "/info"]},
"wallet": {"url": "http://localhost:8003", "endpoints": ["/", "/health", "/wallets"]}, "wallet": {"url": f"http://{API_HOST}:8003", "endpoints": ["/", "/health", "/wallets"]},
"blockchain_rpc": {"url": "http://localhost:8006", "endpoints": ["/health", "/rpc/head", "/rpc/mempool"]}, "blockchain_rpc": {"url": f"http://{API_HOST}:8006", "endpoints": ["/health", "/rpc/head", "/rpc/mempool"]},
} }
@@ -76,10 +79,10 @@ def main():
print("\n⚡ Performance tests...") print("\n⚡ Performance tests...")
perf = test_performance([ perf = test_performance([
("Coordinator", "http://localhost:8000/health"), ("Coordinator", f"http://{API_HOST}:8000/health"),
("Exchange", "http://localhost:8001/api/health"), ("Exchange", f"http://{API_HOST}:8001/api/health"),
("Wallet", "http://localhost:8003/health"), ("Wallet", f"http://{API_HOST}:8003/health"),
("Blockchain RPC", "http://localhost:8006/health"), ("Blockchain RPC", f"http://{API_HOST}:8006/health"),
]) ])
all_results["performance"] = perf all_results["performance"] = perf