build(deps): bump hono from 4.12.18 to 4.12.23 in /plugins/home-assistant-dev/mcp-server #137
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: HA Dev Plugin Tests | |
| on: | |
| push: | |
| branches: [main, testing] | |
| paths: | |
| - "plugins/home-assistant-dev/**" | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - "plugins/home-assistant-dev/**" | |
| defaults: | |
| run: | |
| working-directory: plugins/home-assistant-dev | |
| jobs: | |
| python-tests: | |
| name: Python Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - name: Install dependencies | |
| run: | | |
| pip install pytest pytest-asyncio pytest-cov pyyaml | |
| - name: Run unit tests | |
| run: python3 -m pytest tests/scripts/ -v -m unit --tb=short --cov=scripts --cov-report=term-missing | |
| - name: Run validation tests | |
| run: python3 -m pytest tests/validation/ -v --tb=short | |
| - name: Run structural tests | |
| run: python3 -m pytest tests/test_plugin_structure.py -v --tb=short | |
| integration-tests: | |
| name: Script Integration Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - name: Run scripts against examples | |
| run: bash tests/integration/test_scripts_against_examples.sh | |
| - name: Validate example manifests | |
| run: | | |
| for example in polling-hub minimal-sensor push-integration; do | |
| echo "Validating $example..." | |
| python3 scripts/validate-manifest.py \ | |
| examples/$example/custom_components/*/manifest.json | |
| done | |
| typescript-tests: | |
| name: TypeScript Tests | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: plugins/home-assistant-dev/mcp-server | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| cache-dependency-path: plugins/home-assistant-dev/mcp-server/package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Type check | |
| run: npm run typecheck | |
| - name: Run tests with coverage | |
| run: npm run test:coverage | |
| - name: Build | |
| run: npm run build | |
| structural-validation: | |
| name: Plugin Structure | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - name: Install dependencies | |
| run: pip install pytest pyyaml | |
| - name: Validate plugin structure | |
| run: python3 -m pytest tests/test_plugin_structure.py -v --tb=long | |
| - name: Validate skill frontmatter | |
| run: | | |
| for skill in skills/*/SKILL.md; do | |
| head -1 "$skill" | grep -q '^---' || { echo "FAIL: $skill missing frontmatter"; exit 1; } | |
| done | |
| echo "All skill frontmatters valid" | |
| - name: Validate hooks.json | |
| run: python3 -c "import json; json.load(open('hooks/hooks.json')); print('hooks.json valid')" | |
| mcp-e2e-tests: | |
| name: MCP E2E Tests (HA Container) | |
| runs-on: ubuntu-latest | |
| # Only run on main branch merges or when explicitly labeled | |
| if: github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'run-e2e') | |
| services: | |
| homeassistant: | |
| image: ghcr.io/home-assistant/home-assistant:stable | |
| ports: | |
| - 8123:8123 | |
| options: >- | |
| --health-cmd "curl -sf http://localhost:8123/ -o /dev/null || exit 1" --health-interval 5s --health-timeout 3s --health-retries 60 --health-start-period 60s | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "20" | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - name: Install dependencies | |
| run: | | |
| pip install aiohttp | |
| cd mcp-server && npm ci | |
| - name: Write HA configuration | |
| run: | | |
| # The HA service container needs configuration | |
| # Since we can't easily mount files, use the API after onboarding | |
| echo "HA container starting..." | |
| - name: Complete HA onboarding | |
| run: | | |
| set -e | |
| HA_BASE="http://localhost:8123" | |
| # Wait for HA to be fully ready (healthcheck may pass before onboarding is available) | |
| for i in $(seq 1 30); do | |
| if curl -sf "$HA_BASE/api/onboarding/users" -X POST \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"client_id":"'"$HA_BASE"'/","name":"Test","username":"test","password":"test1234","language":"en"}' \ | |
| > /tmp/ha-onboard.json 2>/dev/null; then | |
| break | |
| fi | |
| echo "Waiting for onboarding endpoint... ($i/30)" | |
| sleep 5 | |
| done | |
| AUTH_CODE=$(python3 -c "import json; print(json.load(open('/tmp/ha-onboard.json'))['auth_code'])") | |
| curl -sf -X POST "$HA_BASE/auth/token" \ | |
| -d "grant_type=authorization_code&code=$AUTH_CODE&client_id=$HA_BASE/" \ | |
| > /tmp/ha-tokens.json | |
| ACCESS_TOKEN=$(python3 -c "import json; print(json.load(open('/tmp/ha-tokens.json'))['access_token'])") | |
| curl -sf -X POST "$HA_BASE/api/onboarding/core_config" \ | |
| -H "Authorization: Bearer $ACCESS_TOKEN" \ | |
| -H "Content-Type: application/json" -d '{}' | |
| curl -sf -X POST "$HA_BASE/api/onboarding/analytics" \ | |
| -H "Authorization: Bearer $ACCESS_TOKEN" \ | |
| -H "Content-Type: application/json" -d '{}' | |
| curl -sf -X POST "$HA_BASE/api/onboarding/integration" \ | |
| -H "Authorization: Bearer $ACCESS_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"client_id":"'"$HA_BASE"'/","redirect_uri":"'"$HA_BASE"'/"}' | |
| # Create LLAT via WebSocket | |
| LLAT=$(python3 << 'PYEOF' | |
| import asyncio, aiohttp, json | |
| async def create_llat(): | |
| async with aiohttp.ClientSession() as session: | |
| async with session.ws_connect("ws://localhost:8123/api/websocket") as ws: | |
| await ws.receive_json() | |
| token = json.load(open("/tmp/ha-tokens.json"))["access_token"] | |
| await ws.send_json({"type": "auth", "access_token": token}) | |
| await ws.receive_json() | |
| await ws.send_json({ | |
| "id": 1, "type": "auth/long_lived_access_token", | |
| "client_name": "CI Test", "lifespan": 1 | |
| }) | |
| msg = await ws.receive_json() | |
| print(msg["result"]) | |
| asyncio.run(create_llat()) | |
| PYEOF | |
| ) | |
| # Write MCP config | |
| mkdir -p ~/.config/ha-dev-mcp | |
| cat > ~/.config/ha-dev-mcp/config.json << EOF | |
| { | |
| "homeAssistant": { "url": "$HA_BASE", "token": "$LLAT" }, | |
| "safety": { | |
| "allowServiceCalls": true, "requireDryRun": false, | |
| "blockedServices": ["homeassistant.restart","homeassistant.stop"] | |
| }, | |
| "features": { "docs": true, "validation": true } | |
| } | |
| EOF | |
| echo "Onboarding complete. HA version:" | |
| curl -sf "$HA_BASE/api/config" -H "Authorization: Bearer $LLAT" | python3 -c "import sys,json; print(json.load(sys.stdin)['version'])" | |
| - name: Configure demo integration | |
| run: | | |
| set -e | |
| HA_BASE="http://localhost:8123" | |
| TOKEN=$(python3 -c "import json,os; c=json.load(open(os.path.expanduser('~/.config/ha-dev-mcp/config.json'))); print(c['homeAssistant']['token'])") | |
| # Initiate demo config flow — capture response body and HTTP status for diagnostics | |
| HTTP_STATUS=$(curl -s -o /tmp/demo_flow.json -w "%{http_code}" \ | |
| -X POST "$HA_BASE/api/config/config_entries/flow" \ | |
| -H "Authorization: Bearer $TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"handler":"demo"}') | |
| echo "Config flow HTTP $HTTP_STATUS:" | |
| cat /tmp/demo_flow.json && echo "" | |
| if [ "$HTTP_STATUS" -lt 200 ] || [ "$HTTP_STATUS" -ge 300 ]; then | |
| echo "ERROR: Failed to initiate demo config flow (HTTP $HTTP_STATUS)" | |
| exit 1 | |
| fi | |
| FLOW_ID=$(python3 -c "import json; print(json.load(open('/tmp/demo_flow.json'))['flow_id'])") | |
| echo "Flow ID: $FLOW_ID" | |
| # Complete the flow (demo requires no user input) | |
| HTTP_STATUS2=$(curl -s -o /tmp/demo_result.json -w "%{http_code}" \ | |
| -X POST "$HA_BASE/api/config/config_entries/flow/$FLOW_ID" \ | |
| -H "Authorization: Bearer $TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{}') | |
| echo "Flow completion HTTP $HTTP_STATUS2:" | |
| cat /tmp/demo_result.json && echo "" | |
| python3 -c "import json; r=json.load(open('/tmp/demo_result.json')); print(f'Demo setup result: {r.get(\"type\", \"unknown\")}')" | |
| - name: Build MCP server | |
| run: cd mcp-server && npx tsc | |
| - name: Wait for demo entities | |
| run: sleep 30 | |
| - name: Run REST API tests | |
| run: node tests/e2e/test-mcp-rest.mjs | |
| - name: Run WebSocket tests | |
| run: node tests/e2e/test-mcp-websocket.mjs |