diff --git a/.github/workflows/codegraph-pr.yml b/.github/workflows/codegraph-pr.yml index 9b4abef..6754ea8 100644 --- a/.github/workflows/codegraph-pr.yml +++ b/.github/workflows/codegraph-pr.yml @@ -35,12 +35,25 @@ jobs: run: | # Resolve the npm-installed binary for this platform BIN="$(npm root -g)/@astudioplus/codegraph-mcp/bin/codegraph-server-linux-x64" - chmod +x "$BIN" - "$BIN" \ - --graph-only \ - --run-tool codegraph_pr_context \ - --tool-args "{\"baseBranch\":\"${{ github.base_ref }}\",\"format\":\"markdown\"}" \ - > review.md 2>/dev/null || echo "CodeGraph review failed" > review.md + # stderr → log (visible in the Actions run); stdout → comment body + # Use origin/ — in a PR checkout the base branch exists as + # a remote-tracking ref, not a local branch. (codegraph 0.17.6+ + # auto-resolves this, but origin/ works on all versions.) + if "$BIN" \ + --graph-only \ + --run-tool codegraph_pr_context \ + --tool-args "{\"baseBranch\":\"origin/${{ github.base_ref }}\",\"format\":\"markdown\"}" \ + > review.md 2> review.err; then + echo "CodeGraph review succeeded" + else + echo "::warning::CodeGraph review failed — see stderr below" + cat review.err + { + echo "## 🔍 CodeGraph PR Review" + echo "" + echo "Review could not be generated for this PR. (CodeGraph)" + } > review.md + fi - name: Post or update PR comment uses: actions/github-script@v7 diff --git a/crates/codegraph-server/src/mcp/server.rs b/crates/codegraph-server/src/mcp/server.rs index 4bd7fa3..77b15ea 100644 --- a/crates/codegraph-server/src/mcp/server.rs +++ b/crates/codegraph-server/src/mcp/server.rs @@ -11,6 +11,8 @@ /// happen in the Rust binary — the JS wrapper handles PostHog ingestion. /// /// Silently dropped if serialization fails (never blocks the server). +/// The wrapper parses these lines from stderr; stdout stays reserved for +/// the JSON-RPC channel. fn emit_tel(value: serde_json::Value) { if let Ok(json) = serde_json::to_string(&value) { eprintln!("TEL: {json}"); @@ -3339,6 +3341,36 @@ impl McpServer { .map(|p| p.to_string_lossy().to_string()) .unwrap_or_else(|| ".".to_string()); + // Resolve the base ref. In CI (GitHub Actions etc.) the + // checkout is a detached HEAD and the base branch only + // exists as a remote-tracking ref (origin/main), not a + // local branch. Try the bare ref first (works locally), + // then fall back to origin/ (works in CI). + let base = { + let probe = tokio::process::Command::new("git") + .args(["rev-parse", "--verify", "--quiet", base]) + .current_dir(&workspace_root) + .output() + .await; + let bare_ok = probe.map(|o| o.status.success()).unwrap_or(false); + if bare_ok { + base.to_string() + } else { + let remote = format!("origin/{}", base); + let probe2 = tokio::process::Command::new("git") + .args(["rev-parse", "--verify", "--quiet", &remote]) + .current_dir(&workspace_root) + .output() + .await; + if probe2.map(|o| o.status.success()).unwrap_or(false) { + remote + } else { + base.to_string() // let the diff surface the error + } + } + }; + let base = base.as_str(); + // ── Step 1: git diff --name-only for file list ── let name_output = tokio::process::Command::new("git") .args(["diff", "--name-only", &format!("{}...HEAD", base)])