fix: gh aw logs MCP tool fails with 'unable to find git executable in PATH' in agent sandbox#26347
fix: gh aw logs MCP tool fails with 'unable to find git executable in PATH' in agent sandbox#26347
Conversation
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/2e2c77ee-7608-4732-a446-2f379ec61ce0 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…o avoid git dependency
When the gh aw logs MCP tool is called in agent sandbox environments where git
is not installed (e.g., audit-workflows agent container), gh run list and gh run
download fail with 'unable to find git executable in PATH' because the gh CLI
cannot determine the current repository without git.
Fix: at the start of DownloadWorkflowLogs, when no explicit --repo override is
specified, fall back to the GITHUB_REPOSITORY environment variable. This value is:
- propagated as opts.RepoOverride to listWorkflowRunsWithPagination (adds --repo
to gh run list)
- propagated as repoOverride to downloadRunArtifactsConcurrent (adds --repo to
gh run download)
- set as GH_REPO env var so gh api calls with {owner}/{repo} template placeholders
(fetchJobStatuses, fetchJobDetails) can resolve the repository without git
Closes #<issue number>
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/2e2c77ee-7608-4732-a446-2f379ec61ce0
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Fixes gh aw logs failures in agent sandbox environments where git isn’t available by ensuring gh commands have an explicit repository context.
Changes:
- Add a fallback in
DownloadWorkflowLogsto useGITHUB_REPOSITORYwhen--repoisn’t provided, and setGH_REPO(if unset) forgh apitemplate paths. - Add a repo-level
.mcp.jsonMCP server configuration forgh aw mcp-server.
Show a summary per file
| File | Description |
|---|---|
| pkg/cli/logs_orchestrator.go | Derives repo context from GITHUB_REPOSITORY when repoOverride is empty; sets GH_REPO to support gh api template placeholders without git. |
| .mcp.json | Adds MCP server config pointing to gh aw mcp-server. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 3
| if os.Getenv("GH_REPO") == "" { | ||
| if err := os.Setenv("GH_REPO", repoOverride); err != nil { | ||
| // Non-fatal: the primary listing and download paths use explicit --repo flags. | ||
| // Only gh api calls using {owner}/{repo} template placeholders (e.g. job status | ||
| // queries) will be unable to resolve the repository context. | ||
| logsOrchestratorLog.Printf("Failed to set GH_REPO env var (gh api {owner}/{repo} template calls may fail to resolve repo context): %v", err) |
There was a problem hiding this comment.
This code mutates the process environment by setting GH_REPO. Since DownloadWorkflowLogs is exported and used from unit tests, leaving GH_REPO set for the remainder of the process can create cross-test contamination and unexpected behavior in later commands. Consider capturing the prior GH_REPO value (via os.LookupEnv) and restoring/unsetting it with a defer so the env change is scoped to this invocation while still affecting all gh subprocesses spawned within the function.
| if os.Getenv("GH_REPO") == "" { | |
| if err := os.Setenv("GH_REPO", repoOverride); err != nil { | |
| // Non-fatal: the primary listing and download paths use explicit --repo flags. | |
| // Only gh api calls using {owner}/{repo} template placeholders (e.g. job status | |
| // queries) will be unable to resolve the repository context. | |
| logsOrchestratorLog.Printf("Failed to set GH_REPO env var (gh api {owner}/{repo} template calls may fail to resolve repo context): %v", err) | |
| if _, ghRepoWasSet := os.LookupEnv("GH_REPO"); !ghRepoWasSet { | |
| if err := os.Setenv("GH_REPO", repoOverride); err != nil { | |
| // Non-fatal: the primary listing and download paths use explicit --repo flags. | |
| // Only gh api calls using {owner}/{repo} template placeholders (e.g. job status | |
| // queries) will be unable to resolve the repository context. | |
| logsOrchestratorLog.Printf("Failed to set GH_REPO env var (gh api {owner}/{repo} template calls may fail to resolve repo context): %v", err) | |
| } else { | |
| defer func() { | |
| if err := os.Unsetenv("GH_REPO"); err != nil { | |
| logsOrchestratorLog.Printf("Failed to restore GH_REPO env var after temporary override: %v", err) | |
| } | |
| }() |
| // DownloadWorkflowLogs downloads and analyzes workflow logs with metrics | ||
| func DownloadWorkflowLogs(ctx context.Context, workflowName string, count int, startDate, endDate, outputDir, engine, ref string, beforeRunID, afterRunID int64, repoOverride string, verbose bool, toolGraph bool, noStaged bool, firewallOnly bool, noFirewall bool, parse bool, jsonOutput bool, timeout int, summaryFile string, safeOutputType string, filteredIntegrity bool, train bool, format string, artifactSets []string) error { | ||
| // When no explicit repo override is given, fall back to GITHUB_REPOSITORY so that | ||
| // `gh run list` and `gh run download` can operate without a git checkout. | ||
| // This is needed in agent sandbox environments (e.g., the audit-workflows agent) | ||
| // where git is not installed but GITHUB_REPOSITORY is set by GitHub Actions. | ||
| // We also set GH_REPO so that `gh api` calls using {owner}/{repo} template | ||
| // placeholders (e.g., fetchJobStatuses, fetchJobDetails) resolve correctly. | ||
| if repoOverride == "" { | ||
| if githubRepo := os.Getenv("GITHUB_REPOSITORY"); githubRepo != "" { | ||
| repoOverride = githubRepo | ||
| logsOrchestratorLog.Printf("Using GITHUB_REPOSITORY env var as repo override: %s", repoOverride) | ||
| if os.Getenv("GH_REPO") == "" { | ||
| if err := os.Setenv("GH_REPO", repoOverride); err != nil { | ||
| // Non-fatal: the primary listing and download paths use explicit --repo flags. | ||
| // Only gh api calls using {owner}/{repo} template placeholders (e.g. job status | ||
| // queries) will be unable to resolve the repository context. | ||
| logsOrchestratorLog.Printf("Failed to set GH_REPO env var (gh api {owner}/{repo} template calls may fail to resolve repo context): %v", err) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
The new GITHUB_REPOSITORY→repoOverride/GH_REPO fallback is a key behavior change but isn’t covered by tests. Suggest extracting the fallback logic into a small helper and adding a unit test that verifies: (1) repoOverride is populated from GITHUB_REPOSITORY when empty, (2) pre-existing GH_REPO is respected, and (3) GH_REPO is set when absent.
| // DownloadWorkflowLogs downloads and analyzes workflow logs with metrics | |
| func DownloadWorkflowLogs(ctx context.Context, workflowName string, count int, startDate, endDate, outputDir, engine, ref string, beforeRunID, afterRunID int64, repoOverride string, verbose bool, toolGraph bool, noStaged bool, firewallOnly bool, noFirewall bool, parse bool, jsonOutput bool, timeout int, summaryFile string, safeOutputType string, filteredIntegrity bool, train bool, format string, artifactSets []string) error { | |
| // When no explicit repo override is given, fall back to GITHUB_REPOSITORY so that | |
| // `gh run list` and `gh run download` can operate without a git checkout. | |
| // This is needed in agent sandbox environments (e.g., the audit-workflows agent) | |
| // where git is not installed but GITHUB_REPOSITORY is set by GitHub Actions. | |
| // We also set GH_REPO so that `gh api` calls using {owner}/{repo} template | |
| // placeholders (e.g., fetchJobStatuses, fetchJobDetails) resolve correctly. | |
| if repoOverride == "" { | |
| if githubRepo := os.Getenv("GITHUB_REPOSITORY"); githubRepo != "" { | |
| repoOverride = githubRepo | |
| logsOrchestratorLog.Printf("Using GITHUB_REPOSITORY env var as repo override: %s", repoOverride) | |
| if os.Getenv("GH_REPO") == "" { | |
| if err := os.Setenv("GH_REPO", repoOverride); err != nil { | |
| // Non-fatal: the primary listing and download paths use explicit --repo flags. | |
| // Only gh api calls using {owner}/{repo} template placeholders (e.g. job status | |
| // queries) will be unable to resolve the repository context. | |
| logsOrchestratorLog.Printf("Failed to set GH_REPO env var (gh api {owner}/{repo} template calls may fail to resolve repo context): %v", err) | |
| } | |
| } | |
| } | |
| } | |
| // applyRepoOverrideFallback populates repoOverride from GITHUB_REPOSITORY when unset | |
| // and ensures GH_REPO is available for gh api {owner}/{repo} template expansion. | |
| func applyRepoOverrideFallback(repoOverride string) string { | |
| if repoOverride != "" { | |
| return repoOverride | |
| } | |
| githubRepo := os.Getenv("GITHUB_REPOSITORY") | |
| if githubRepo == "" { | |
| return repoOverride | |
| } | |
| repoOverride = githubRepo | |
| logsOrchestratorLog.Printf("Using GITHUB_REPOSITORY env var as repo override: %s", repoOverride) | |
| if os.Getenv("GH_REPO") == "" { | |
| if err := os.Setenv("GH_REPO", repoOverride); err != nil { | |
| // Non-fatal: the primary listing and download paths use explicit --repo flags. | |
| // Only gh api calls using {owner}/{repo} template placeholders (e.g. job status | |
| // queries) will be unable to resolve the repository context. | |
| logsOrchestratorLog.Printf("Failed to set GH_REPO env var (gh api {owner}/{repo} template calls may fail to resolve repo context): %v", err) | |
| } | |
| } | |
| return repoOverride | |
| } | |
| // DownloadWorkflowLogs downloads and analyzes workflow logs with metrics | |
| func DownloadWorkflowLogs(ctx context.Context, workflowName string, count int, startDate, endDate, outputDir, engine, ref string, beforeRunID, afterRunID int64, repoOverride string, verbose bool, toolGraph bool, noStaged bool, firewallOnly bool, noFirewall bool, parse bool, jsonOutput bool, timeout int, summaryFile string, safeOutputType string, filteredIntegrity bool, train bool, format string, artifactSets []string) error { | |
| repoOverride = applyRepoOverrideFallback(repoOverride) |
| { | ||
| "servers": { | ||
| "github-agentic-workflows": { | ||
| "command": "gh", | ||
| "args": ["aw", "mcp-server"] | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
This PR adds a new top-level .mcp.json file, but the PR description only mentions changes to pkg/cli/logs_orchestrator.go. Please update the PR description to mention .mcp.json, or drop the file from this PR if it’s unrelated to the reported gh aw logs sandbox failure.
|
@copilot review comments |
…h defer, add tests, remove .mcp.json - Extract GITHUB_REPOSITORY fallback into applyRepoOverrideFallback helper for easier testing and cleaner DownloadWorkflowLogs function - Use defer to unset GH_REPO when the function returns, preventing cross-test contamination in unit tests and across sequential CLI invocations - Use os.LookupEnv to detect pre-existing GH_REPO (never overwrite it) - Add TestApplyRepoOverrideFallback covering all four scenarios: explicit override, GITHUB_REPOSITORY fallback + cleanup, pre-existing GH_REPO respected, and no env vars present - Remove unrelated .mcp.json file from the PR Agent-Logs-Url: https://github.com/github/gh-aw/sessions/231e43cf-03b9-4cd9-8605-752ef9f39035 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Addressed all three review comments in commit 5004613:
|
|
@copilot do not set env, fail if needed |
Per review feedback, do not set environment variables; let gh api {owner}/{repo}
calls fail gracefully (they already return nil on error). Simplify:
- applyRepoOverrideFallback returns string only (no cleanup func)
- DownloadWorkflowLogs uses a simple assignment, no defer cleanup
- Update tests to match — remove GH_REPO behaviour coverage
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/12bca3e1-ec6b-4574-ada9-24b948ab4e3c
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/12bca3e1-ec6b-4574-ada9-24b948ab4e3c Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done in e016b3a. |
|
@copilot review comments |
Code review ran clean — the only finding was about |
The
gh aw logsMCP tool is unusable in agent sandbox environments (e.g.audit-workflows) becausegh run listtries to detect the current repo viagit remote, butgitis not in PATH inside the agent container.Changes
pkg/cli/logs_orchestrator.go: Extracts aapplyRepoOverrideFallbackhelper that, whenrepoOverrideis empty, falls back toGITHUB_REPOSITORY(always set in GitHub Actions). This single assignment propagates through the entire call chain:listWorkflowRunsWithPagination→ adds--repotogh run listdownloadRunArtifactsConcurrent→ adds--repotogh run downloadand artifact API callsThe fallback only activates when no explicit
--repoflag is given.gh apicalls using{owner}/{repo}template placeholders (fetchJobStatuses,fetchJobDetails) may fail if git is unavailable andGH_REPOis not set externally, but these calls are already non-fatal and return nil on error.pkg/cli/logs_repo_fallback_test.go: Unit tests forapplyRepoOverrideFallbackcovering explicit override passthrough,GITHUB_REPOSITORYfallback, and no env vars present.