Test Installer Scripts #224
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: Test Installer Scripts | |
| on: | |
| push: | |
| paths: | |
| - 'scripts/install.sh' | |
| - 'scripts/install-macos.sh' | |
| - 'scripts/install-linux.sh' | |
| - 'scripts/install-windows.ps1' | |
| - '.github/workflows/test-installer-scripts.yml' | |
| pull_request: | |
| paths: | |
| - 'scripts/install.sh' | |
| - 'scripts/install-macos.sh' | |
| - 'scripts/install-linux.sh' | |
| - 'scripts/install-windows.ps1' | |
| - '.github/workflows/test-installer-scripts.yml' | |
| schedule: | |
| - cron: '0 21 * * *' # Daily at 4pm Eastern | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| test-macos: | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Run macOS installer script (first run) | |
| run: sh scripts/install.sh | |
| - name: Run macOS installer script (second run - idempotency test) | |
| run: | | |
| # Should skip uv install and show "uv is already installed" | |
| sh scripts/install.sh | |
| - name: Verify uv is installed | |
| run: | | |
| command -v uvx || (echo "uvx not found" && exit 1) | |
| uvx --version | |
| - name: Verify Claude Desktop config was created | |
| run: | | |
| CONFIG_FILE="$HOME/Library/Application Support/Claude/claude_desktop_config.json" | |
| if [ ! -f "$CONFIG_FILE" ]; then | |
| echo "Config file not created" | |
| exit 1 | |
| fi | |
| cat "$CONFIG_FILE" | |
| # Verify it contains Home Assistant config | |
| grep -q '"Home Assistant"' "$CONFIG_FILE" || (echo "Home Assistant config not found" && exit 1) | |
| grep -q 'ha-mcp@latest' "$CONFIG_FILE" || (echo "ha-mcp not configured" && exit 1) | |
| test-linux: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Run Linux installer script (first run) | |
| run: sh scripts/install.sh | |
| - name: Run Linux installer script (second run - idempotency test) | |
| run: | | |
| # Should skip uv install and show "uv is already installed" | |
| sh scripts/install.sh | |
| - name: Verify uv is installed | |
| run: | | |
| export PATH="$HOME/.local/bin:$PATH" | |
| command -v uvx || (echo "uvx not found" && exit 1) | |
| uvx --version | |
| - name: Verify Claude Desktop config was created | |
| run: | | |
| CONFIG_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/Claude/claude_desktop_config.json" | |
| if [ ! -f "$CONFIG_FILE" ]; then | |
| echo "Config file not created" | |
| exit 1 | |
| fi | |
| cat "$CONFIG_FILE" | |
| # Verify it contains Home Assistant config | |
| grep -q '"Home Assistant"' "$CONFIG_FILE" || (echo "Home Assistant config not found" && exit 1) | |
| grep -q 'ha-mcp@latest' "$CONFIG_FILE" || (echo "ha-mcp not configured" && exit 1) | |
| - name: Merge preserves other MCP servers and writes valid JSON | |
| run: | | |
| CONFIG_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/Claude/claude_desktop_config.json" | |
| # Seed an unrelated MCP server, then re-run; the merge must keep it. | |
| python3 -c "import json; c=json.load(open('$CONFIG_FILE')); c['mcpServers']['Keepme']={'command':'x'}; json.dump(c, open('$CONFIG_FILE','w'))" | |
| sh scripts/install.sh | |
| python3 -c "import json; c=json.load(open('$CONFIG_FILE')); assert 'Keepme' in c['mcpServers'], 'merge dropped an existing MCP server'; assert 'Home Assistant' in c['mcpServers']; print('OK: merge preserved other servers and JSON is valid')" | |
| test-windows: | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Run Windows installer script (first run) | |
| shell: pwsh | |
| run: | | |
| & ./scripts/install-windows.ps1 | |
| - name: Run Windows installer script (second run - idempotency test) | |
| shell: pwsh | |
| run: | | |
| # Should skip uv install and show "uv is already installed" | |
| & ./scripts/install-windows.ps1 | |
| - name: Verify uv is installed | |
| shell: pwsh | |
| run: | | |
| # Refresh PATH (winget installs to user PATH which doesn't persist between steps) | |
| $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") | |
| $uvx = Get-Command uvx -ErrorAction SilentlyContinue | |
| if (-not $uvx) { | |
| Write-Error "uvx not found" | |
| exit 1 | |
| } | |
| uvx --version | |
| - name: Verify Claude Desktop config was created | |
| shell: pwsh | |
| run: | | |
| $ConfigFile = "$env:APPDATA\Claude\claude_desktop_config.json" | |
| if (-not (Test-Path $ConfigFile)) { | |
| Write-Error "Config file not created" | |
| exit 1 | |
| } | |
| Get-Content $ConfigFile | |
| # Verify it contains Home Assistant config | |
| $content = Get-Content $ConfigFile -Raw | |
| if ($content -notmatch '"Home Assistant"') { | |
| Write-Error "Home Assistant config not found" | |
| exit 1 | |
| } | |
| if ($content -notmatch 'ha-mcp@latest') { | |
| Write-Error "ha-mcp not configured" | |
| exit 1 | |
| } | |
| check-demo-env: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check demo environment is up | |
| run: | | |
| # Verify the demo Home Assistant server is responding | |
| curl -sf "https://ha-mcp-demo-server.qc-h.net/" -o /dev/null | |
| echo "Demo server is up and responding" | |
| test-claude-code-args: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: --claude-code must not write a Claude Desktop config | |
| run: | | |
| # Stub a 'claude' binary so the --claude-code branch runs without a real install. | |
| mkdir -p "$HOME/bin" | |
| printf '#!/bin/sh\nexit 0\n' > "$HOME/bin/claude" | |
| chmod +x "$HOME/bin/claude" | |
| export PATH="$HOME/bin:$PATH" | |
| sh scripts/install.sh --claude-code | |
| CONFIG_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/Claude/claude_desktop_config.json" | |
| if [ -f "$CONFIG_FILE" ]; then | |
| echo "BUG: --claude-code wrote a Claude Desktop config"; cat "$CONFIG_FILE"; exit 1 | |
| fi | |
| echo "OK: --claude-code did not create a Desktop config" | |
| - name: --help advertises --claude-code and exits 0 | |
| run: | | |
| out=$(sh scripts/install.sh --help) | |
| echo "$out" | grep -q -- '--claude-code' || (echo "help missing --claude-code" && exit 1) | |
| test-wsl-guard: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: WSL is detected and the Desktop path is refused (no config written) | |
| run: | | |
| set +e | |
| WSL_DISTRO_NAME=Ubuntu sh scripts/install.sh | |
| rc=$? | |
| set -e | |
| [ "$rc" -eq 1 ] || (echo "Expected exit 1 under WSL, got $rc" && exit 1) | |
| CONFIG_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/Claude/claude_desktop_config.json" | |
| [ ! -f "$CONFIG_FILE" ] || (echo "WSL guard wrote a config anyway" && exit 1) | |
| echo "OK: WSL Desktop path refused, no config written" | |
| - name: WSL + --claude-code is allowed past the guard | |
| run: | | |
| # Stub claude so the allowed path doesn't do a real install. | |
| mkdir -p "$HOME/bin" | |
| printf '#!/bin/sh\nexit 0\n' > "$HOME/bin/claude" | |
| chmod +x "$HOME/bin/claude" | |
| export PATH="$HOME/bin:$PATH" | |
| out=$(WSL_DISTRO_NAME=Ubuntu sh scripts/install.sh --claude-code 2>&1) || true | |
| if echo "$out" | grep -qi 'Detected WSL'; then | |
| echo "guard wrongly fired for --claude-code under WSL"; exit 1 | |
| fi | |
| echo "OK: --claude-code not blocked under WSL" | |
| test-stale-uv: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Stale uv without uvx falls through to the upgrade path (issue #1593) | |
| run: | | |
| set -e | |
| # Reproduce a pre-uvx uv: a `uv` binary on PATH with no `uvx` beside | |
| # it. The old `uv || uvx` gate accepted this stale uv and then | |
| # hard-exited at the uvx lookup; the gate now keys on uvx, so this | |
| # must instead reach the install/upgrade path and end with a uvx. | |
| STALE="$(mktemp -d)" | |
| printf '#!/bin/sh\necho "uv 0.1.39"\n' > "$STALE/uv" | |
| chmod +x "$STALE/uv" | |
| # Curated PATH: the stale uv stub plus the system dirs the installer | |
| # needs (sh, curl, python3), and crucially no real uvx. A real uvx | |
| # leaking onto PATH would mask the bug, so fail loudly if present. | |
| export PATH="$STALE:/usr/bin:/bin" | |
| command -v uv >/dev/null || { echo "precondition: uv stub not on PATH"; exit 1; } | |
| if command -v uvx >/dev/null; then | |
| echo "precondition failed: uvx unexpectedly on PATH"; exit 1 | |
| fi | |
| sh scripts/install.sh | |
| # The upgrade path must have produced a working uvx. | |
| export PATH="$HOME/.local/bin:$PATH" | |
| command -v uvx >/dev/null || { echo "uvx missing after upgrade path"; exit 1; } | |
| echo "OK: stale uv triggered the upgrade path, uvx now available" |