|
| 1 | +#!/bin/bash |
| 2 | +set -ex |
| 3 | + |
| 4 | +# Initialize default exit code |
| 5 | +RESULT=1 |
| 6 | + |
| 7 | +# Cleanup function to be called on exit |
| 8 | +cleanup() { |
| 9 | + set +x |
| 10 | + echo "Cleaning up artifacts..." |
| 11 | + docker stop itk-service > /dev/null 2>&1 || true |
| 12 | + docker rm itk-service > /dev/null 2>&1 || true |
| 13 | + docker rmi itk_service > /dev/null 2>&1 || true |
| 14 | + rm -rf a2a-samples > /dev/null 2>&1 || true |
| 15 | + rm -rf pyproto > /dev/null 2>&1 || true |
| 16 | + rm -f instruction.proto > /dev/null 2>&1 || true |
| 17 | + echo "Done. Final exit code: $RESULT" |
| 18 | +} |
| 19 | + |
| 20 | +# Register cleanup function to run on script exit |
| 21 | +trap cleanup EXIT |
| 22 | + |
| 23 | +# 1. Pull a2a-samples and checkout revision |
| 24 | +: "${A2A_SAMPLES_REVISION:=$(grep "A2A_SAMPLES_REVISION:" ../.github/workflows/itk.yaml | head -n 1 | awk '{print $2}')}" |
| 25 | + |
| 26 | +if [ ! -d "a2a-samples" ]; then |
| 27 | + git clone https://github.com/a2aproject/a2a-samples.git a2a-samples |
| 28 | +fi |
| 29 | +cd a2a-samples |
| 30 | +git fetch origin |
| 31 | +git checkout "$A2A_SAMPLES_REVISION" |
| 32 | + |
| 33 | +# Only pull if it's a branch (not a detached HEAD) |
| 34 | +if git symbolic-ref -q HEAD > /dev/null; then |
| 35 | + git pull origin "$A2A_SAMPLES_REVISION" |
| 36 | +fi |
| 37 | +cd .. |
| 38 | + |
| 39 | +# 2. Copy instruction.proto from a2a-samples |
| 40 | +cp a2a-samples/itk/protos/instruction.proto ./instruction.proto |
| 41 | + |
| 42 | +# 3. Build pyproto library |
| 43 | +mkdir -p pyproto |
| 44 | +touch pyproto/__init__.py |
| 45 | +uv run --with grpcio-tools python -m grpc_tools.protoc \ |
| 46 | + -I. \ |
| 47 | + --python_out=pyproto \ |
| 48 | + --grpc_python_out=pyproto \ |
| 49 | + instruction.proto |
| 50 | + |
| 51 | +# Fix imports in generated file |
| 52 | +sed -i 's/^import instruction_pb2 as instruction__pb2/from . import instruction_pb2 as instruction__pb2/' pyproto/instruction_pb2_grpc.py |
| 53 | + |
| 54 | +# 4. Build jit itk_service docker image from root of a2a-samples/itk |
| 55 | +# We run docker build from the itk directory inside a2a-samples |
| 56 | +docker build -t itk_service a2a-samples/itk |
| 57 | + |
| 58 | +# 5. Start docker service |
| 59 | +# Mounting a2a-python as repo and itk as current agent |
| 60 | +A2A_PYTHON_ROOT=$(cd .. && pwd) |
| 61 | +ITK_DIR=$(pwd) |
| 62 | +REPO_ROOT=$(cd ../.. && pwd) |
| 63 | + |
| 64 | +# Stop existing container if any |
| 65 | +docker rm -f itk-service || true |
| 66 | + |
| 67 | +docker run -d --name itk-service \ |
| 68 | + -v "$A2A_PYTHON_ROOT:/app/agents/repo" \ |
| 69 | + -v "$ITK_DIR:/app/agents/repo/itk" \ |
| 70 | + -v "$REPO_ROOT/.git:/app/agents/repo/.git" \ |
| 71 | + -p 8000:8000 \ |
| 72 | + itk_service |
| 73 | + |
| 74 | +# 5.1. Fix dubious ownership for git (needed for uv-dynamic-versioning) |
| 75 | +docker exec itk-service git config --global --add safe.directory /app/agents/repo |
| 76 | +docker exec itk-service git config --global --add safe.directory /app/agents/repo/itk |
| 77 | +docker exec itk-service git config --global --add safe.directory /app/agents/repo/.git |
| 78 | + |
| 79 | +# 6. Verify service is up and send post request |
| 80 | +MAX_RETRIES=30 |
| 81 | +echo "Waiting for ITK service to start on 127.0.0.1:8000..." |
| 82 | +set +e |
| 83 | +for i in $(seq 1 $MAX_RETRIES); do |
| 84 | + if curl -s http://127.0.0.1:8000/ > /dev/null; then |
| 85 | + echo "Service is up!" |
| 86 | + break |
| 87 | + fi |
| 88 | + echo "Still waiting... ($i/$MAX_RETRIES)" |
| 89 | + sleep 2 |
| 90 | +done |
| 91 | + |
| 92 | +# If we reached the end of the loop without success |
| 93 | +if ! curl -s http://127.0.0.1:8000/ > /dev/null; then |
| 94 | + echo "Error: ITK service failed to start on port 8000" |
| 95 | + docker logs itk-service |
| 96 | + exit 1 |
| 97 | +fi |
| 98 | + |
| 99 | +echo "ITK Service is up! Sending compatibility test request..." |
| 100 | +RESPONSE=$(curl -s -X POST http://127.0.0.1:8000/run \ |
| 101 | + -H "Content-Type: application/json" \ |
| 102 | + -d '{ |
| 103 | + "tests": [ |
| 104 | + { |
| 105 | + "name": "Star Topology (Full) - JSONRPC & GRPC", |
| 106 | + "sdks": ["current", "python_v10", "python_v03", "go_v10", "go_v03"], |
| 107 | + "traversal": "euler", |
| 108 | + "edges": ["0->1", "0->2", "0->3", "0->4", "1->0", "2->0", "3->0", "4->0"], |
| 109 | + "protocols": ["jsonrpc", "grpc"] |
| 110 | + }, |
| 111 | + { |
| 112 | + "name": "Star Topology (No Go v03) - HTTP_JSON", |
| 113 | + "sdks": ["current", "python_v10", "python_v03", "go_v10"], |
| 114 | + "traversal": "euler", |
| 115 | + "edges": ["0->1", "0->2", "0->3", "1->0", "2->0", "3->0"], |
| 116 | + "protocols": ["http_json"] |
| 117 | + }, |
| 118 | + { |
| 119 | + "name": "Star Topology (Full) - JSONRPC & GRPC (Streaming)", |
| 120 | + "sdks": ["current", "python_v10", "python_v03", "go_v10", "go_v03"], |
| 121 | + "traversal": "euler", |
| 122 | + "edges": ["0->1", "0->2", "0->3", "0->4", "1->0", "2->0", "3->0", "4->0"], |
| 123 | + "protocols": ["jsonrpc", "grpc"], |
| 124 | + "streaming": true |
| 125 | + }, |
| 126 | + { |
| 127 | + "name": "Star Topology (No Go v03) - HTTP_JSON (Streaming)", |
| 128 | + "sdks": ["current", "python_v10", "python_v03", "go_v10"], |
| 129 | + "traversal": "euler", |
| 130 | + "edges": ["0->1", "0->2", "0->3", "1->0", "2->0", "3->0"], |
| 131 | + "protocols": ["http_json"], |
| 132 | + "streaming": true |
| 133 | + } |
| 134 | + ] |
| 135 | + }') |
| 136 | + |
| 137 | +echo "--------------------------------------------------------" |
| 138 | +echo "ITK TEST RESULTS:" |
| 139 | +echo "--------------------------------------------------------" |
| 140 | +echo "$RESPONSE" | python3 -c " |
| 141 | +import sys, json |
| 142 | +try: |
| 143 | + data = json.load(sys.stdin) |
| 144 | + all_passed = data.get('all_passed', False) |
| 145 | + results = data.get('results', {}) |
| 146 | + for test, passed in results.items(): |
| 147 | + status = 'PASSED' if passed else 'FAILED' |
| 148 | + print(f'{test}: {status}') |
| 149 | + print('--------------------------------------------------------') |
| 150 | + print(f'OVERALL STATUS: {\"PASSED\" if all_passed else \"FAILED\"}') |
| 151 | + if not all_passed: |
| 152 | + sys.exit(1) |
| 153 | +except Exception as e: |
| 154 | + print(f'Error parsing results: {e}') |
| 155 | + print(f'Raw response: {data if \"data\" in locals() else \"no data\"}') |
| 156 | + sys.exit(1) |
| 157 | +" |
| 158 | +RESULT=$? |
| 159 | +set -e |
| 160 | + |
| 161 | +if [ $RESULT -ne 0 ]; then |
| 162 | + echo "Tests failed. Container logs:" |
| 163 | + docker logs itk-service |
| 164 | +fi |
| 165 | +echo "--------------------------------------------------------" |
| 166 | + |
| 167 | +# Final exit result will be captured by trap cleanup |
| 168 | +exit $RESULT |
| 169 | + |
0 commit comments