Skip to content

Commit f5d358a

Browse files
committed
fix: CODEQL_PATH tests and startup validation
- Fix codeql-path-tests CI job that timed out (~17min) on all three OSes due to bash pipe PID tracking bug (`$!` captured subshell PID, not server PID). Replace inline workflow steps with portable bash scripts using background watchdog + `wait` pattern (no GNU `timeout`). - Add startup-time validation (`validateCodeQLBinaryReachable`) that runs `codeql version --format=terse` before tool registration. Server now fails fast with actionable error when codeql is not on PATH and CODEQL_PATH is not set. - Add Test 3: verify server fails at startup when codeql is missing from both PATH and CODEQL_PATH. - Address 5 Copilot PR review comments: - Implement caching in `resolveCodeQLBinary()` (short-circuit on repeat calls) - Fix JSDoc in temp-dir.ts (`<repoRoot>` -> `<packageRoot>`) - Gate Windows backslash test to `process.platform === 'win32'` - Use `path.isAbsolute()` for cross-platform database path check - Guard `additionalPacksPath` with `existsSync()` for npm-installed layouts Scripts: server/scripts/test-codeql-path-{invalid,missing,valid}.sh Tests: 375 passed (4 new)
1 parent 57c569d commit f5d358a

12 files changed

+535
-99
lines changed

.github/workflows/client-integration-tests.yml

Lines changed: 10 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -276,87 +276,19 @@ jobs:
276276
## to a non-existent file and codeql is not on PATH.
277277
- name: CODEQL_PATH Tests - Test 1 - Fail with invalid CODEQL_PATH
278278
shell: bash
279-
run: |
280-
echo "=== Test 1: Server must fail when CODEQL_PATH is invalid ==="
281-
export PATH="$CLEAN_PATH"
282-
export CODEQL_PATH="/nonexistent/path/to/codeql"
283-
284-
# Use a long-running Node process as a stdin source to prevent
285-
# immediate EOF (which causes a clean exit instead of a crash).
286-
# Cross-platform: works on Linux, macOS, and Windows (no mkfifo).
287-
node -e "setTimeout(()=>{},999999)" | \
288-
node server/dist/codeql-development-mcp-server.js >stdout.txt 2>stderr.txt &
289-
SERVER_PID=$!
290-
291-
# Wait for the process to exit (it should fail within seconds)
292-
sleep 5
293-
294-
if kill -0 "$SERVER_PID" 2>/dev/null; then
295-
echo "::error::Server should have exited but is still running"
296-
kill "$SERVER_PID" 2>/dev/null || true
297-
wait "$SERVER_PID" 2>/dev/null || true
298-
cat stderr.txt
299-
exit 1
300-
fi
301-
302-
wait "$SERVER_PID" 2>/dev/null
303-
EXIT_CODE=$?
279+
run: ./server/scripts/test-codeql-path-invalid.sh
304280

305-
echo "Exit code: $EXIT_CODE"
306-
cat stderr.txt
307-
308-
if [[ "$EXIT_CODE" -ne 0 ]] && grep -qi "does not exist" stderr.txt; then
309-
echo ""
310-
echo "✅ PASS: Server failed at startup with expected CODEQL_PATH error"
311-
else
312-
echo ""
313-
echo "::error::Unexpected behavior (exit=$EXIT_CODE)"
314-
exit 1
315-
fi
281+
## Test 2: The server must fail at startup when codeql is not on PATH
282+
## and CODEQL_PATH is not set.
283+
- name: CODEQL_PATH Tests - Test 2 - Fail when codeql not on PATH and CODEQL_PATH not set
284+
shell: bash
285+
run: ./server/scripts/test-codeql-path-missing.sh
316286

317-
## Test 2: The server must start without error when CODEQL_PATH points
287+
## Test 3: The server must start without error when CODEQL_PATH points
318288
## to a valid CodeQL binary, even though codeql is not on PATH.
319-
- name: CODEQL_PATH Tests - Test 2 - Start with valid CODEQL_PATH
289+
- name: CODEQL_PATH Tests - Test 3 - Start with valid CODEQL_PATH
320290
shell: bash
321-
run: |
322-
echo "=== Test 2: Server must start when CODEQL_PATH is valid ==="
323-
export PATH="$CLEAN_PATH"
324-
export CODEQL_PATH="${{ steps.locate-codeql.outputs.codeql-binary }}"
325-
326-
echo "CODEQL_PATH=$CODEQL_PATH"
327-
echo "codeql in PATH: $(command -v codeql 2>/dev/null || echo 'not found')"
328-
329-
# Use a long-running Node process as a stdin source to keep the
330-
# STDIO transport alive. Cross-platform (no mkfifo needed).
331-
node -e "setTimeout(()=>{},999999)" | \
332-
node server/dist/codeql-development-mcp-server.js >stdout.txt 2>stderr.txt &
333-
SERVER_PID=$!
334-
335-
# Give the server time to either start successfully or crash
336-
sleep 5
337-
338-
if kill -0 "$SERVER_PID" 2>/dev/null; then
339-
echo "✅ PASS: Server is running after 5 seconds (PID $SERVER_PID)"
340-
echo "--- startup logs ---"
341-
cat stderr.txt
342-
343-
# Verify stderr confirms CODEQL_PATH was used
344-
if grep -q "CODEQL_PATH" stderr.txt; then
345-
echo "✅ Server logged CODEQL_PATH resolution"
346-
else
347-
echo "⚠️ CODEQL_PATH not found in stderr (non-fatal)"
348-
fi
349-
350-
# Clean up
351-
kill "$SERVER_PID" 2>/dev/null || true
352-
wait "$SERVER_PID" 2>/dev/null || true
353-
else
354-
wait "$SERVER_PID" 2>/dev/null
355-
EXIT_CODE=$?
356-
echo "::error::Server exited prematurely with code $EXIT_CODE"
357-
cat stderr.txt
358-
exit 1
359-
fi
291+
run: ./server/scripts/test-codeql-path-valid.sh "${{ steps.locate-codeql.outputs.codeql-binary }}"
360292

361293
- name: CODEQL_PATH Tests - Summary
362294
shell: bash
@@ -366,6 +298,7 @@ jobs:
366298
echo "| Test | Result |" >> $GITHUB_STEP_SUMMARY
367299
echo "| ---- | ------ |" >> $GITHUB_STEP_SUMMARY
368300
echo "| Invalid CODEQL_PATH causes startup failure | ✅ Pass |" >> $GITHUB_STEP_SUMMARY
301+
echo "| Missing codeql (no PATH, no CODEQL_PATH) causes startup failure | ✅ Pass |" >> $GITHUB_STEP_SUMMARY
369302
echo "| Valid CODEQL_PATH (no PATH) starts server | ✅ Pass |" >> $GITHUB_STEP_SUMMARY
370303
echo "" >> $GITHUB_STEP_SUMMARY
371304
echo "| Detail | Value |" >> $GITHUB_STEP_SUMMARY

server/dist/codeql-development-mcp-server.js

Lines changed: 35 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/dist/codeql-development-mcp-server.js.map

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
##
5+
## Test: Server must fail at startup when CODEQL_PATH points to a non-existent file.
6+
##
7+
## Usage:
8+
## ./server/scripts/test-codeql-path-invalid.sh [<server-bundle>]
9+
##
10+
## Arguments:
11+
## <server-bundle> Path to the server JS bundle (default: server/dist/codeql-development-mcp-server.js)
12+
##
13+
## Environment:
14+
## CLEAN_PATH Optional. When set, PATH is replaced with this value so that
15+
## codeql is not discoverable via PATH. When unset, the current
16+
## PATH is used as-is (useful for local testing).
17+
##
18+
## Exit codes:
19+
## 0 Server failed at startup with the expected CODEQL_PATH error
20+
## 1 Unexpected behavior (server did not fail, wrong error, etc.)
21+
##
22+
23+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24+
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
25+
26+
SERVER_BUNDLE="${1:-server/dist/codeql-development-mcp-server.js}"
27+
28+
# Resolve relative paths against the repo root
29+
if [[ ! "${SERVER_BUNDLE}" = /* ]]; then
30+
SERVER_BUNDLE="${REPO_ROOT}/${SERVER_BUNDLE}"
31+
fi
32+
33+
if [[ ! -f "${SERVER_BUNDLE}" ]]; then
34+
echo "::error::Server bundle not found: ${SERVER_BUNDLE}"
35+
exit 1
36+
fi
37+
38+
# Replace PATH with CLEAN_PATH when provided
39+
if [[ -n "${CLEAN_PATH:-}" ]]; then
40+
export PATH="${CLEAN_PATH}"
41+
fi
42+
43+
# Point CODEQL_PATH at a non-existent file
44+
export CODEQL_PATH="/nonexistent/path/to/codeql"
45+
46+
echo "=== Test: Server must fail when CODEQL_PATH is invalid ==="
47+
echo " SERVER_BUNDLE=${SERVER_BUNDLE}"
48+
echo " CODEQL_PATH=${CODEQL_PATH}"
49+
50+
# Run the server directly (no pipe) with a watchdog timeout.
51+
# Feed /dev/null to stdin so the STDIO transport gets immediate EOF and the
52+
# process can exit cleanly after the startup error.
53+
# NOTE: We avoid GNU `timeout` because it may not be available on macOS CI runners.
54+
STDOUT_FILE="$(mktemp)"
55+
STDERR_FILE="$(mktemp)"
56+
cleanup() { rm -f "${STDOUT_FILE}" "${STDERR_FILE}"; }
57+
trap cleanup EXIT
58+
59+
node "${SERVER_BUNDLE}" \
60+
< /dev/null \
61+
> "${STDOUT_FILE}" \
62+
2> "${STDERR_FILE}" &
63+
SERVER_PID=$!
64+
65+
# Watchdog: kill the server if it hasn't exited within 15 seconds
66+
( sleep 15; kill "${SERVER_PID}" 2>/dev/null ) &
67+
WATCHDOG_PID=$!
68+
69+
wait "${SERVER_PID}" 2>/dev/null && EXIT_CODE=0 || EXIT_CODE=$?
70+
71+
# Cancel the watchdog if the server exited on its own
72+
kill "${WATCHDOG_PID}" 2>/dev/null || true
73+
wait "${WATCHDOG_PID}" 2>/dev/null || true
74+
75+
echo " Exit code: ${EXIT_CODE}"
76+
77+
if [[ "${EXIT_CODE}" -ne 0 ]] && grep -qi "does not exist" "${STDERR_FILE}"; then
78+
echo ""
79+
echo "--- stderr (first 20 lines) ---"
80+
head -20 "${STDERR_FILE}"
81+
echo ""
82+
echo "✅ PASS: Server failed at startup with expected CODEQL_PATH error"
83+
exit 0
84+
else
85+
echo ""
86+
echo "::error::Unexpected behavior (exit=${EXIT_CODE})"
87+
echo "--- stderr ---"
88+
cat "${STDERR_FILE}"
89+
echo "--- stdout ---"
90+
cat "${STDOUT_FILE}"
91+
exit 1
92+
fi

0 commit comments

Comments
 (0)