From 982ef02dcdf5974f79875f4fa1c564c160fff3fa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Jun 2026 03:51:36 +0000 Subject: [PATCH 1/4] Initial plan From 6798c15cdfb8c54e6a595de3dc9774179a17404b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Jun 2026 04:06:00 +0000 Subject: [PATCH 2/4] Initial progress check - no changes yet Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../smoke-copilot-aoai-entra.lock.yml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/smoke-copilot-aoai-entra.lock.yml b/.github/workflows/smoke-copilot-aoai-entra.lock.yml index 4718430c760..4b991a5c414 100644 --- a/.github/workflows/smoke-copilot-aoai-entra.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-entra.lock.yml @@ -1718,7 +1718,7 @@ jobs: DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_MCP_SCRIPTS_PORT -e GH_AW_MCP_SCRIPTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -e GH_TOKEN -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.3.25' - mkdir -p /home/runner/.copilot + mkdir -p "$HOME/.copilot" GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) cat << GH_AW_MCP_CONFIG_51d567a2fd4f7ca5_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { @@ -1849,9 +1849,11 @@ jobs: run: | set -o pipefail printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt - trap 'rm -f /home/runner/.copilot/settings.json' EXIT - mkdir -p /home/runner/.copilot - printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > /home/runner/.copilot/settings.json + trap 'rm -f "$HOME/.copilot/settings.json"' EXIT + mkdir -p "$HOME/.copilot" + printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" + export XDG_CONFIG_HOME="$HOME" + export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json" touch /tmp/gh-aw/agent-step-summary.md GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) export GH_AW_NODE_BIN @@ -1889,7 +1891,6 @@ jobs: COPILOT_PROVIDER_BASE_URL: ${{ secrets.FOUNDRY_OPENAI_ENDPOINT }} COPILOT_PROVIDER_WIRE_API: responses GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} - GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} @@ -1910,7 +1911,6 @@ jobs: GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com GIT_COMMITTER_NAME: github-actions[bot] RUNNER_TEMP: ${{ runner.temp }} - XDG_CONFIG_HOME: /home/runner - name: Stop CLI Proxy if: always() continue-on-error: true @@ -2452,7 +2452,7 @@ jobs: if: always() && steps.detection_guard.outputs.run_detection == 'true' run: | rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json" - rm -f /home/runner/.copilot/mcp-config.json + rm -f "$HOME/.copilot/mcp-config.json" rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" - name: Prepare threat detection files if: always() && steps.detection_guard.outputs.run_detection == 'true' @@ -2510,9 +2510,10 @@ jobs: run: | set -o pipefail printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt - trap 'rm -f /home/runner/.copilot/settings.json' EXIT - mkdir -p /home/runner/.copilot - printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > /home/runner/.copilot/settings.json + trap 'rm -f "$HOME/.copilot/settings.json"' EXIT + mkdir -p "$HOME/.copilot" + printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" + export XDG_CONFIG_HOME="$HOME" touch /tmp/gh-aw/agent-step-summary.md GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) export GH_AW_NODE_BIN @@ -2567,7 +2568,6 @@ jobs: GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com GIT_COMMITTER_NAME: github-actions[bot] RUNNER_TEMP: ${{ runner.temp }} - XDG_CONFIG_HOME: /home/runner - name: Parse threat detection token usage for step summary id: parse_detection_token_usage if: always() From cc59cc6d3f9e35ce28190ed73a2c3edef3655c71 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Jun 2026 04:37:21 +0000 Subject: [PATCH 3/4] chore: Extract hard-coded file paths to constants (120 instances) Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/add_package_manifest.go | 25 ++--- pkg/cli/audit.go | 2 +- pkg/cli/audit_comparison.go | 3 +- pkg/cli/experiments_command.go | 2 +- pkg/cli/fetch.go | 3 +- pkg/cli/firewall_policy.go | 3 +- pkg/cli/git.go | 10 +- pkg/cli/includes.go | 2 +- pkg/cli/logs_run_processor.go | 2 +- pkg/cli/mcp_tools_privileged.go | 7 +- pkg/cli/shell_completion.go | 22 ++++- pkg/cli/trial_repository.go | 2 +- pkg/constants/constants.go | 2 +- pkg/constants/path_constants.go | 94 +++++++++++++++++++ .../hardcodedfilepath/hardcodedfilepath.go | 15 ++- pkg/parser/import_field_extractor.go | 3 +- pkg/parser/include_expander.go | 3 +- pkg/parser/mcp.go | 2 +- pkg/parser/remote_fetch.go | 12 +-- pkg/workflow/agentic_engine.go | 2 +- pkg/workflow/antigravity_engine.go | 4 +- pkg/workflow/antigravity_logs.go | 3 +- pkg/workflow/antigravity_mcp.go | 2 +- pkg/workflow/awf_helpers.go | 6 +- pkg/workflow/claude_engine.go | 4 +- pkg/workflow/claude_mcp.go | 2 +- pkg/workflow/codex_engine.go | 8 +- pkg/workflow/codex_mcp.go | 2 +- pkg/workflow/compiler_difc_proxy.go | 4 +- pkg/workflow/compiler_main_job.go | 2 +- pkg/workflow/compiler_safe_outputs_job.go | 4 +- pkg/workflow/compiler_yaml.go | 4 +- pkg/workflow/compiler_yaml_main_job.go | 28 +++--- pkg/workflow/copilot_engine.go | 2 +- pkg/workflow/copilot_engine_execution.go | 12 ++- pkg/workflow/copilot_logs.go | 3 +- pkg/workflow/crush_engine.go | 4 +- pkg/workflow/crush_mcp.go | 3 +- pkg/workflow/engine_output.go | 3 +- .../frontmatter_extraction_metadata.go | 3 +- pkg/workflow/gemini_engine.go | 6 +- pkg/workflow/gemini_logs.go | 3 +- pkg/workflow/gemini_mcp.go | 2 +- .../mcp_config_playwright_renderer.go | 7 +- pkg/workflow/mcp_environment.go | 2 +- pkg/workflow/mcp_renderer_builtin.go | 4 +- pkg/workflow/opencode_engine.go | 4 +- pkg/workflow/opencode_mcp.go | 3 +- pkg/workflow/pi_engine.go | 8 +- pkg/workflow/repo_memory.go | 8 +- pkg/workflow/runtime_import_validation.go | 5 +- pkg/workflow/safe_jobs.go | 4 +- .../safe_outputs_config_generation.go | 3 +- pkg/workflow/safe_outputs_steps.go | 2 +- pkg/workflow/setup_action_paths.go | 22 ++++- pkg/workflow/step_order_validation.go | 4 +- pkg/workflow/threat_detection.go | 2 +- 57 files changed, 283 insertions(+), 125 deletions(-) create mode 100644 pkg/constants/path_constants.go diff --git a/pkg/cli/add_package_manifest.go b/pkg/cli/add_package_manifest.go index f289c2ebfea..40422fed5f5 100644 --- a/pkg/cli/add_package_manifest.go +++ b/pkg/cli/add_package_manifest.go @@ -10,6 +10,7 @@ import ( "github.com/goccy/go-yaml" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" "github.com/github/gh-aw/pkg/parser" "github.com/github/gh-aw/pkg/semverutil" @@ -30,7 +31,7 @@ var getRepositoryPackageDefaultBranch = resolveRepositoryPackageDefaultBranch var getRepositoryPackageLatestRelease = resolveRepositoryPackageLatestRelease var addPackageManifestLog = logger.New("cli:add_package_manifest") -var packageSourceDirectories = []string{"workflows", ".github/workflows"} +var packageSourceDirectories = []string{"workflows", constants.DotGithubWorkflowsDir} const repositoryPackageManifestFileName = "aw.yml" const repositoryPackageManifestVersion = "1" @@ -496,13 +497,13 @@ func isSupportedManifestIncludePath(p string) bool { func isSupportedSkillDirectoryPrefix(cleaned string) bool { return strings.HasPrefix(cleaned, packageSkillsDirectory+"/") || - strings.HasPrefix(cleaned, ".github/"+packageSkillsDirectory+"/") + strings.HasPrefix(cleaned, constants.DotGithubDir+packageSkillsDirectory+"/") } func skillDirectoryRoot(cleaned string) string { switch { - case strings.HasPrefix(cleaned, ".github/"+packageSkillsDirectory+"/"): - return ".github/" + packageSkillsDirectory + case strings.HasPrefix(cleaned, constants.DotGithubDir+packageSkillsDirectory+"/"): + return constants.DotGithubDir + packageSkillsDirectory default: return packageSkillsDirectory } @@ -510,13 +511,13 @@ func skillDirectoryRoot(cleaned string) string { func isSupportedAgentDirectoryPrefix(cleaned string) bool { return strings.HasPrefix(cleaned, packageAgentsDirectory+"/") || - strings.HasPrefix(cleaned, ".github/"+packageAgentsDirectory+"/") + strings.HasPrefix(cleaned, constants.DotGithubDir+packageAgentsDirectory+"/") } func agentDirectoryRoot(cleaned string) string { switch { - case strings.HasPrefix(cleaned, ".github/"+packageAgentsDirectory+"/"): - return ".github/" + packageAgentsDirectory + case strings.HasPrefix(cleaned, constants.DotGithubDir+packageAgentsDirectory+"/"): + return constants.DotGithubDir + packageAgentsDirectory default: return packageAgentsDirectory } @@ -620,7 +621,7 @@ func resolvePackageAgentFiles(owner, repo, packagePath, ref, host string, explic } var agentFiles []string - for _, root := range []string{packageAgentsDirectory, ".github/" + packageAgentsDirectory} { + for _, root := range []string{packageAgentsDirectory, constants.DotGithubDir + packageAgentsDirectory} { agentsDir := joinRepositoryPackagePath(packagePath, root) files, err := listPackageDirFilesForHost(owner, repo, ref, agentsDir, host) if err != nil { @@ -642,7 +643,7 @@ func resolvePackageAgentFiles(owner, repo, packagePath, ref, host string, explic // of skill subdirectories (those that contain a SKILL.md file). func scanPackageSkillDirs(owner, repo, packagePath, ref, host string) ([]string, error) { var skillDirs []string - for _, root := range []string{packageSkillsDirectory, ".github/" + packageSkillsDirectory} { + for _, root := range []string{packageSkillsDirectory, constants.DotGithubDir + packageSkillsDirectory} { skillsDir := joinRepositoryPackagePath(packagePath, root) subdirs, err := listPackageDirSubdirsForHost(owner, repo, ref, skillsDir, host) if err != nil { @@ -755,14 +756,14 @@ func isSupportedPackageInstallablePath(p string) bool { if strings.HasSuffix(lowerCleaned, ".md") { return strings.HasPrefix(cleaned, "workflows/") || strings.HasPrefix(cleaned, "agentic-workflows/") || - strings.HasPrefix(cleaned, ".github/workflows/") + strings.HasPrefix(cleaned, constants.DotGithubWorkflowsDir+"/") } if isActionWorkflowPath(cleaned) { - if !strings.HasPrefix(cleaned, ".github/workflows/") { + if !strings.HasPrefix(cleaned, constants.DotGithubWorkflowsDir+"/") { return false } // Reject nested subdirectories: only direct children of .github/workflows/ are allowed. - remaining := strings.TrimPrefix(cleaned, ".github/workflows/") + remaining := strings.TrimPrefix(cleaned, constants.DotGithubWorkflowsDir+"/") return !strings.Contains(remaining, "/") } return false diff --git a/pkg/cli/audit.go b/pkg/cli/audit.go index e6d647504d3..aa9414e08ab 100644 --- a/pkg/cli/audit.go +++ b/pkg/cli/audit.go @@ -1103,7 +1103,7 @@ func fetchWorkflowRunMetadata(ctx context.Context, runID int64, owner, repo, hos // When the GitHub API returns the workflow file path as the run's name (e.g. for runs // that were cancelled or failed before any jobs started), resolve the actual workflow // display name so that audit output is consistent with 'gh aw logs'. - if strings.HasPrefix(run.WorkflowName, ".github/") { + if strings.HasPrefix(run.WorkflowName, constants.DotGithubDir) { if displayName := resolveWorkflowDisplayName(ctx, run.WorkflowPath, owner, repo, hostname); displayName != "" { auditLog.Printf("Resolved workflow display name: %q -> %q", run.WorkflowName, displayName) run.WorkflowName = displayName diff --git a/pkg/cli/audit_comparison.go b/pkg/cli/audit_comparison.go index 68c6a416cfb..d78bfa5c299 100644 --- a/pkg/cli/audit_comparison.go +++ b/pkg/cli/audit_comparison.go @@ -11,6 +11,7 @@ import ( "sort" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" "github.com/github/gh-aw/pkg/workflow" ) @@ -513,7 +514,7 @@ func findPreviousSuccessfulWorkflowRuns(ctx context.Context, current WorkflowRun } for index := range runs { - if strings.HasPrefix(runs[index].WorkflowName, ".github/") { + if strings.HasPrefix(runs[index].WorkflowName, constants.DotGithubDir) { if displayName := resolveWorkflowDisplayName(ctx, runs[index].WorkflowPath, owner, repo, hostname); displayName != "" { runs[index].WorkflowName = displayName } diff --git a/pkg/cli/experiments_command.go b/pkg/cli/experiments_command.go index a91c8e86a15..31e510dd192 100644 --- a/pkg/cli/experiments_command.go +++ b/pkg/cli/experiments_command.go @@ -369,7 +369,7 @@ func loadRemoteExperimentConfigs(repoOverride, experimentName string) map[string } for _, candidate := range candidates { - apiPath := ".github/workflows/" + candidate + ".md" + apiPath := constants.DotGithubWorkflowsDir + "/" + candidate + ".md" args := []string{"api", "repos/{owner}/{repo}/contents/" + url.PathEscape(apiPath), "--jq", ".content", diff --git a/pkg/cli/fetch.go b/pkg/cli/fetch.go index 8c4a57ef6a5..4dc70adc804 100644 --- a/pkg/cli/fetch.go +++ b/pkg/cli/fetch.go @@ -11,6 +11,7 @@ import ( "time" "github.com/github/gh-aw/pkg/console" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" "github.com/github/gh-aw/pkg/parser" "github.com/github/gh-aw/pkg/stringutil" @@ -122,7 +123,7 @@ func fetchRemoteWorkflow(ctx context.Context, spec *WorkflowSpec, verbose bool) // Try with common workflow directory prefixes if the direct path fails. // This handles short workflow names without path separators (e.g. "my-workflow.md"). if !strings.HasPrefix(spec.WorkflowPath, "workflows/") && !strings.Contains(spec.WorkflowPath, "/") { - for _, prefix := range []string{"workflows/", ".github/workflows/"} { + for _, prefix := range []string{"workflows/", constants.DotGithubWorkflowsDir + "/"} { altPath := prefix + spec.WorkflowPath if !strings.HasSuffix(altPath, ".md") { altPath += ".md" diff --git a/pkg/cli/firewall_policy.go b/pkg/cli/firewall_policy.go index 48ddea342b1..a32068e033b 100644 --- a/pkg/cli/firewall_policy.go +++ b/pkg/cli/firewall_policy.go @@ -10,6 +10,7 @@ import ( "slices" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" ) @@ -461,7 +462,7 @@ func detectFirewallAuditArtifacts(runDir string) (manifestPath, auditJSONLPath s if !checkDir(filepath.Join(agentDir, "sandbox", "firewall", "audit"), agentBase+"/sandbox/firewall/audit") { // Old artifact structure (/tmp/gh-aw/ prefix preserved inside the artifact): // /tmp/gh-aw/sandbox/firewall/audit/ - checkDir(filepath.Join(agentDir, "tmp", "gh-aw", "sandbox", "firewall", "audit"), agentBase+"/tmp/gh-aw/sandbox/firewall/audit") + checkDir(filepath.Join(agentDir, "tmp", "gh-aw", "sandbox", "firewall", "audit"), agentBase+constants.AWFAuditDir) } if manifestPath != "" && auditJSONLPath != "" { return manifestPath, auditJSONLPath, nil diff --git a/pkg/cli/git.go b/pkg/cli/git.go index c54d9de0730..8aa8c4c9b0b 100644 --- a/pkg/cli/git.go +++ b/pkg/cli/git.go @@ -300,14 +300,14 @@ func getRepositorySlugFromRemoteForPath(path string) string { func stageWorkflowChanges() { // Find git root and add .github/workflows relative to it if gitRoot, err := gitutil.FindGitRoot(); err == nil { - workflowsPath := filepath.Join(gitRoot, ".github/workflows/") + workflowsPath := filepath.Join(gitRoot, constants.DotGithubWorkflowsDir+"/") _ = exec.Command("git", "-C", gitRoot, "add", workflowsPath).Run() // Also stage .gitattributes if it was modified _ = stageGitAttributesIfChanged() } else { // Fallback to relative path if git root can't be found - _ = exec.Command("git", "add", ".github/workflows/").Run() + _ = exec.Command("git", "add", constants.DotGithubWorkflowsDir+"/").Run() _ = exec.Command("git", "add", ".gitattributes").Run() } } @@ -322,7 +322,7 @@ func ensureGitAttributes() (bool, error) { } gitAttributesPath := filepath.Join(gitRoot, ".gitattributes") - lockYmlEntry := ".github/workflows/*.lock.yml linguist-generated=true merge=ours" + lockYmlEntry := constants.DotGithubWorkflowsDir + "/*.lock.yml linguist-generated=true merge=ours" requiredEntries := []string{lockYmlEntry} // Read existing .gitattributes file if it exists @@ -344,7 +344,7 @@ func ensureGitAttributes() (bool, error) { break } // Check for old format entries that need updating - if strings.HasPrefix(trimmedLine, ".github/workflows/*.lock.yml") && required == lockYmlEntry { + if strings.HasPrefix(trimmedLine, constants.DotGithubWorkflowsDir+"/*.lock.yml") && required == lockYmlEntry { gitLog.Print("Updating old .gitattributes entry format") lines[i] = lockYmlEntry found = true @@ -401,7 +401,7 @@ func ensureLogsGitignore() error { // Check if .gitignore already exists if _, err := os.Stat(gitignorePath); err == nil { - gitLog.Print(".github/aw/logs/.gitignore already exists") + gitLog.Print(constants.DotGithubDir + "aw/logs/.gitignore already exists") return nil } diff --git a/pkg/cli/includes.go b/pkg/cli/includes.go index 70c72db89d6..8b70020a31a 100644 --- a/pkg/cli/includes.go +++ b/pkg/cli/includes.go @@ -91,7 +91,7 @@ func FetchIncludeFromSource(includePath string, baseSpec *WorkflowSpec, verbose // If it's a relative path starting with shared/, it's relative to .github/ var fullPath string if strings.HasPrefix(filePath, "shared/") { - fullPath = ".github/" + filePath + fullPath = constants.DotGithubDir + filePath } else { // Otherwise, resolve relative to the workflow path directory baseDir := getParentDir(baseSpec.WorkflowPath) diff --git a/pkg/cli/logs_run_processor.go b/pkg/cli/logs_run_processor.go index e6e364e3333..9ef15569d6c 100644 --- a/pkg/cli/logs_run_processor.go +++ b/pkg/cli/logs_run_processor.go @@ -531,7 +531,7 @@ func inferWorkflowPathFromDisplayName(displayName string) string { if slug == "" { return "" } - return ".github/workflows/" + slug + ".lock.yml" + return constants.DotGithubWorkflowsDir + "/" + slug + ".lock.yml" } // runHasDifcFilteredItems checks if a run's gateway logs contain any DIFC_FILTERED events. diff --git a/pkg/cli/mcp_tools_privileged.go b/pkg/cli/mcp_tools_privileged.go index 6009af0695d..689d322a396 100644 --- a/pkg/cli/mcp_tools_privileged.go +++ b/pkg/cli/mcp_tools_privileged.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/modelcontextprotocol/go-sdk/jsonrpc" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -133,7 +134,7 @@ from where the previous request stopped due to timeout.`, // Build command arguments // Force output directory to /tmp/gh-aw/aw-mcp/logs for MCP server - cmdArgs := []string{"logs", "-o", "/tmp/gh-aw/aw-mcp/logs"} + cmdArgs := []string{"logs", "-o", constants.AwMCPLogsDir} if args.WorkflowName != "" { cmdArgs = append(cmdArgs, args.WorkflowName) } @@ -384,7 +385,7 @@ Multi-run diff returns JSON describing changes between the base and each compari // Pass all run IDs/URLs directly - the audit command handles single vs. diff mode. cmdArgs := []string{"audit"} cmdArgs = append(cmdArgs, runItems...) - cmdArgs = append(cmdArgs, "-o", "/tmp/gh-aw/aw-mcp/logs", "--json") + cmdArgs = append(cmdArgs, "-o", constants.AwMCPLogsDir, "--json") if len(args.Artifacts) > 0 { cmdArgs = append(cmdArgs, "--artifacts", strings.Join(args.Artifacts, ",")) } @@ -525,7 +526,7 @@ Returns JSON describing the differences between the base run and each comparison // Build: gh aw audit diff -o ... --json [--artifacts ...] cmdArgs := []string{"audit", "diff", args.BaseRunID} cmdArgs = append(cmdArgs, args.CompareRunIDs...) - cmdArgs = append(cmdArgs, "-o", "/tmp/gh-aw/aw-mcp/logs", "--json") + cmdArgs = append(cmdArgs, "-o", constants.AwMCPLogsDir, "--json") if len(args.Artifacts) > 0 { cmdArgs = append(cmdArgs, "--artifacts", strings.Join(args.Artifacts, ",")) } diff --git a/pkg/cli/shell_completion.go b/pkg/cli/shell_completion.go index e16059822ac..b6adb8524f9 100644 --- a/pkg/cli/shell_completion.go +++ b/pkg/cli/shell_completion.go @@ -30,6 +30,18 @@ const ( ShellUnknown ShellType = "unknown" ) +// homebrewPrefixOptHomebrew is the default Homebrew prefix on Apple Silicon Macs. +const homebrewPrefixOptHomebrew = "/opt/homebrew" + +// homebrewPrefixUsrLocal is the default Homebrew prefix on Intel Macs. +const homebrewPrefixUsrLocal = "/usr/local" + +// etcBashCompletionDir is the system-wide bash completion directory on Linux. +const etcBashCompletionDir = "/etc/bash_completion.d" + +// etcBashCompletionGhAw is the system-wide bash completion file path for gh-aw. +const etcBashCompletionGhAw = etcBashCompletionDir + "/gh-aw" + // DetectShell detects the current shell from environment variables func DetectShell() ShellType { shellCompletionLog.Print("Detecting current shell") @@ -145,7 +157,7 @@ func installBashCompletion(verbose bool, cmd *cobra.Command) error { brewPrefix := os.Getenv("HOMEBREW_PREFIX") if brewPrefix == "" { // Try common locations - for _, prefix := range []string{"/opt/homebrew", "/usr/local"} { + for _, prefix := range []string{homebrewPrefixOptHomebrew, homebrewPrefixUsrLocal} { if _, err := os.Stat(filepath.Join(prefix, "etc", "bash_completion.d")); err == nil { brewPrefix = prefix break @@ -159,8 +171,8 @@ func installBashCompletion(verbose bool, cmd *cobra.Command) error { } } else { // Linux - if _, err := os.Stat("/etc/bash_completion.d"); err == nil { - completionPath = "/etc/bash_completion.d/gh-aw" + if _, err := os.Stat(etcBashCompletionDir); err == nil { + completionPath = etcBashCompletionGhAw } else { completionPath = filepath.Join(homeDir, ".bash_completion.d", "gh-aw") } @@ -426,7 +438,7 @@ func uninstallBashCompletion(verbose bool) error { if runtime.GOOS == "darwin" { brewPrefix := os.Getenv("HOMEBREW_PREFIX") if brewPrefix == "" { - for _, prefix := range []string{"/opt/homebrew", "/usr/local"} { + for _, prefix := range []string{homebrewPrefixOptHomebrew, homebrewPrefixUsrLocal} { if _, err := os.Stat(filepath.Join(prefix, "etc", "bash_completion.d")); err == nil { possiblePaths = append(possiblePaths, filepath.Join(prefix, "etc", "bash_completion.d", "gh-aw")) } @@ -438,7 +450,7 @@ func uninstallBashCompletion(verbose bool) error { // System-wide installations (Linux) if runtime.GOOS == "linux" { - possiblePaths = append(possiblePaths, "/etc/bash_completion.d/gh-aw") + possiblePaths = append(possiblePaths, etcBashCompletionGhAw) } removed := false diff --git a/pkg/cli/trial_repository.go b/pkg/cli/trial_repository.go index f7d22aae70a..7bffdc89d51 100644 --- a/pkg/cli/trial_repository.go +++ b/pkg/cli/trial_repository.go @@ -312,7 +312,7 @@ func installWorkflowInTrialMode(ctx context.Context, tempDir string, parsedSpec // Compile the workflow with trial modifications config := CompileConfig{ - MarkdownFiles: []string{".github/workflows/" + parsedSpec.WorkflowName + ".md"}, + MarkdownFiles: []string{constants.DotGithubWorkflowsDir + "/" + parsedSpec.WorkflowName + ".md"}, Verbose: opts.Verbose, EngineOverride: opts.EngineOverride, Validate: true, diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 6f96aa5a19b..d8a0243686f 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -359,7 +359,7 @@ func GetWorkflowDir() string { if dir := os.Getenv("GH_AW_WORKFLOWS_DIR"); dir != "" { return filepath.ToSlash(dir) } - return ".github/workflows" + return DotGithubWorkflowsDir } // MaxSymlinkDepth limits recursive symlink resolution when fetching remote files. diff --git a/pkg/constants/path_constants.go b/pkg/constants/path_constants.go new file mode 100644 index 00000000000..f28d713d651 --- /dev/null +++ b/pkg/constants/path_constants.go @@ -0,0 +1,94 @@ +package constants + +// Repository (dot-github) path constants. +// These are repository-relative paths used for GitHub configuration files. + +// DotGithubDir is the repository-relative path to the .github directory (with trailing slash). +// Use for prefix comparisons and directory operations. +const DotGithubDir = ".github/" + +// DotGithubWorkflowsDir is the default repository-relative path to the workflows directory, +// without a trailing slash. Callers that need the trailing slash should append "/". +// Use GetWorkflowDir() to allow override via the GH_AW_WORKFLOWS_DIR environment variable. +const DotGithubWorkflowsDir = ".github/workflows" + +// DotGithubAgentsDir is the repository-relative path to the .github/agents directory, +// without a trailing slash. Callers that need the trailing slash should append "/". +const DotGithubAgentsDir = ".github/agents" + +// Container-side temporary path constants (/tmp/gh-aw/...). +// These are paths within the agent container filesystem, distinct from the +// runner-side paths rooted at ${RUNNER_TEMP}/gh-aw (see GhAwRootDirShell). + +// TmpGhAwDir is the base directory for gh-aw temporary files within the container +// filesystem, without a trailing slash. +const TmpGhAwDir = "/tmp/gh-aw" + +// AgentStdioLogPath is the path where the agent process stdout/stderr is logged. +const AgentStdioLogPath = "/tmp/gh-aw/agent-stdio.log" + +// AgentDir is the directory for agent-generated output files, without a trailing slash. +const AgentDir = "/tmp/gh-aw/agent" + +// AgentPromptFilePath is the path to the prompt file passed to the AI engine. +const AgentPromptFilePath = "/tmp/gh-aw/aw-prompts/prompt.txt" + +// MCPLogsDir is the base directory for MCP server logs, without a trailing slash. +const MCPLogsDir = "/tmp/gh-aw/mcp-logs" + +// MCPLogsSafeOutputsDir is the directory for safe-outputs MCP server logs. +const MCPLogsSafeOutputsDir = "/tmp/gh-aw/mcp-logs/safeoutputs" + +// MCPLogsPlaywrightDir is the directory for Playwright MCP server logs. +const MCPLogsPlaywrightDir = "/tmp/gh-aw/mcp-logs/playwright" + +// MCPScriptsLogsDir is the base directory for MCP scripts logs, without a trailing slash. +const MCPScriptsLogsDir = "/tmp/gh-aw/mcp-scripts/logs" + +// MCPConfigDir is the directory for MCP configuration files, without a trailing slash. +const MCPConfigDir = "/tmp/gh-aw/mcp-config" + +// MCPConfigLogsDir is the directory for MCP configuration logs, without a trailing slash. +const MCPConfigLogsDir = "/tmp/gh-aw/mcp-config/logs" + +// MCPConfigServersFilePath is the path to the MCP servers configuration file in the container. +const MCPConfigServersFilePath = "/tmp/gh-aw/mcp-config/mcp-servers.json" + +// AwMCPLogsDir is the directory for agentic-workflows MCP server logs. +const AwMCPLogsDir = "/tmp/gh-aw/aw-mcp/logs" + +// CommentMemoryDir is the directory for comment memory persistence, without a trailing slash. +const CommentMemoryDir = "/tmp/gh-aw/comment-memory" + +// RepoMemoryDir is the base directory for repository memory persistence, without a trailing slash. +const RepoMemoryDir = "/tmp/gh-aw/repo-memory" + +// ThreatDetectionLogPath is the path for the threat detection log file. +const ThreatDetectionLogPath = "/tmp/gh-aw/threat-detection/detection.log" + +// PiAgentDir is the directory for Pi agent configuration files. +const PiAgentDir = "/tmp/gh-aw/pi-agent-dir" + +// AntigravityClientErrorGlob is the glob pattern for Antigravity client error JSON files. +const AntigravityClientErrorGlob = "/tmp/gh-aw/antigravity-client-error-*.json" + +// GeminiClientErrorGlob is the glob pattern for Gemini client error JSON files. +const GeminiClientErrorGlob = "/tmp/gh-aw/gemini-client-error-*.json" + +// AwPatchGlob is the glob pattern for agent-generated git patch files. +const AwPatchGlob = "/tmp/gh-aw/aw-*.patch" + +// AwBundleGlob is the glob pattern for agent-generated git bundle files. +const AwBundleGlob = "/tmp/gh-aw/aw-*.bundle" + +// DifcProxyTLSCACertPath is the path to the DIFC proxy TLS CA certificate file. +const DifcProxyTLSCACertPath = "/tmp/gh-aw/difc-proxy-tls/ca.crt" + +// ProxyLogsDir is the directory for proxy logs, without a trailing slash. +const ProxyLogsDir = "/tmp/gh-aw/proxy-logs" + +// ProxyLogsTLSCACertPath is the path to the proxy TLS CA certificate file. +const ProxyLogsTLSCACertPath = "/tmp/gh-aw/proxy-logs/proxy-tls/ca.crt" + +// SandboxAgentLogsDir is the directory for sandbox agent logs, without a trailing slash. +const SandboxAgentLogsDir = "/tmp/gh-aw/sandbox/agent/logs" diff --git a/pkg/linters/hardcodedfilepath/hardcodedfilepath.go b/pkg/linters/hardcodedfilepath/hardcodedfilepath.go index 2f5484fdfb9..88d08a49b03 100644 --- a/pkg/linters/hardcodedfilepath/hardcodedfilepath.go +++ b/pkg/linters/hardcodedfilepath/hardcodedfilepath.go @@ -54,9 +54,9 @@ func (r constRef) String() string { // ".github", or "${RUNNER_TEMP}" alone are not flagged. var pathPrefixes = []string{ "/tmp/", - "${RUNNER_TEMP}/", - "${{ runner.temp }}/", - ".github/", + pathPrefixRunnerTempShell, + pathPrefixRunnerTempGHAExpr, + pathPrefixDotGithub, "/opt/", "/usr/", "/var/", @@ -65,6 +65,15 @@ var pathPrefixes = []string{ "/run/", } +// Path prefix constants used to define the pathPrefixes list. +// These are declared as constants so that the var declaration above uses +// constant references (not string literals), avoiding self-flagging. +const ( + pathPrefixRunnerTempShell = "${RUNNER_TEMP}/" + pathPrefixRunnerTempGHAExpr = "${{ runner.temp }}/" + pathPrefixDotGithub = ".github/" +) + // minPathRuneLen is the minimum character length of an unquoted path value // to flag. Paths shorter than this are considered too generic. const minPathRuneLen = 8 diff --git a/pkg/parser/import_field_extractor.go b/pkg/parser/import_field_extractor.go index 30b300955e5..26ec0d72454 100644 --- a/pkg/parser/import_field_extractor.go +++ b/pkg/parser/import_field_extractor.go @@ -12,6 +12,7 @@ import ( "regexp" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/importinpututil" ) @@ -960,7 +961,7 @@ func computeImportRelPath(fullPath, importPath string) string { if idx := strings.LastIndex(normalizedFullPath, "/.github/"); idx >= 0 { return normalizedFullPath[idx+1:] // +1 to skip the leading slash } - if strings.HasPrefix(normalizedFullPath, ".github/") { + if strings.HasPrefix(normalizedFullPath, constants.DotGithubDir) { return normalizedFullPath } return importPath diff --git a/pkg/parser/include_expander.go b/pkg/parser/include_expander.go index 21d3b41f508..a843706ed4b 100644 --- a/pkg/parser/include_expander.go +++ b/pkg/parser/include_expander.go @@ -8,6 +8,7 @@ import ( "regexp" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" ) @@ -192,7 +193,7 @@ func ExtractBodyLevelImportPaths(content, baseDir string) []BodyLevelImport { // Convert relative paths to workspace-root-relative. // Paths already starting with ".github/" are workspace-root-relative. // Absolute paths are used as-is. - if !strings.HasPrefix(importPath, ".github/") && !filepath.IsAbs(importPath) { + if !strings.HasPrefix(importPath, constants.DotGithubDir) && !filepath.IsAbs(importPath) { if repoRoot != "" { fullPath := filepath.Join(baseDir, importPath) if rel, err := filepath.Rel(repoRoot, fullPath); err == nil && !strings.HasPrefix(rel, "..") { diff --git a/pkg/parser/mcp.go b/pkg/parser/mcp.go index 250ea162e85..4f8debbf526 100644 --- a/pkg/parser/mcp.go +++ b/pkg/parser/mcp.go @@ -343,7 +343,7 @@ func buildPlaywrightBuiltinConfig(toolValue any) RegistryMCPServerConfig { Command: "docker", Args: []string{ "run", "-i", "--rm", "--shm-size=2gb", "--cap-add=SYS_ADMIN", - "-v", "/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs", + "-v", constants.MCPLogsDir + ":" + constants.MCPLogsDir, "mcr.microsoft.com/playwright:" + string(constants.DefaultPlaywrightBrowserVersion), }, Env: make(map[string]string), diff --git a/pkg/parser/remote_fetch.go b/pkg/parser/remote_fetch.go index c1cff456d3e..88a05d85786 100644 --- a/pkg/parser/remote_fetch.go +++ b/pkg/parser/remote_fetch.go @@ -108,12 +108,12 @@ func isUnderWorkflowsDirectory(filePath string) bool { normalizedPath := filepath.ToSlash(filePath) // Check if the path contains .github/workflows/ - if !strings.Contains(normalizedPath, ".github/workflows/") { + if !strings.Contains(normalizedPath, constants.DotGithubWorkflowsDir+"/") { return false } // Extract the part after .github/workflows/ - parts := strings.Split(normalizedPath, ".github/workflows/") + parts := strings.Split(normalizedPath, constants.DotGithubWorkflowsDir+"/") if len(parts) < 2 { return false } @@ -133,7 +133,7 @@ func isCustomAgentFile(filePath string) bool { normalizedPath := filepath.ToSlash(filePath) // Check if the path contains .github/agents/ and ends with .md - return strings.Contains(normalizedPath, ".github/agents/") && strings.HasSuffix(strings.ToLower(normalizedPath), ".md") + return strings.Contains(normalizedPath, constants.DotGithubAgentsDir+"/") && strings.HasSuffix(strings.ToLower(normalizedPath), ".md") } // isRepositoryImport checks if an import spec is a repository-only import (no file path) @@ -238,10 +238,10 @@ func computeIncludeResolveAndSecurityBases(filePath, baseDir string) (string, st if strings.HasSuffix(githubFolder, ".github") { repoRoot := filepath.Dir(githubFolder) filePathSlash := filepath.ToSlash(filePath) - if strings.HasPrefix(filePathSlash, ".github/") { + if strings.HasPrefix(filePathSlash, constants.DotGithubDir) { resolveBase = repoRoot } else if stripped, ok := strings.CutPrefix(filePathSlash, "/"); ok { - if !strings.HasPrefix(stripped, ".github/") && !strings.HasPrefix(stripped, ".agents/") { + if !strings.HasPrefix(stripped, constants.DotGithubDir) && !strings.HasPrefix(stripped, ".agents/") { return "", "", filePath } normalizedFilePath = filepath.FromSlash(stripped) @@ -258,7 +258,7 @@ func computeIncludeResolveAndSecurityBases(filePath, baseDir string) (string, st func resolveAndValidateLocalIncludePath(filePath, resolveBase, securityBase string) (string, error) { if stripped, ok := strings.CutPrefix(filepath.ToSlash(filePath), "/"); ok { - if !strings.HasPrefix(stripped, ".github/") && !strings.HasPrefix(stripped, ".agents/") { + if !strings.HasPrefix(stripped, constants.DotGithubDir) && !strings.HasPrefix(stripped, ".agents/") { remoteLog.Printf("Security: Path not within .github or .agents: %s", filePath) return "", fmt.Errorf("security: path %s must be within .github or .agents folder", filePath) } diff --git a/pkg/workflow/agentic_engine.go b/pkg/workflow/agentic_engine.go index 854f4fecd96..1eba394adf8 100644 --- a/pkg/workflow/agentic_engine.go +++ b/pkg/workflow/agentic_engine.go @@ -345,7 +345,7 @@ func (e *BaseEngine) GetModelEnvVarName() string { // Engines can override this to use engine-specific log files func (e *BaseEngine) GetLogFileForParsing() string { // Default to agent-stdio.log which contains stdout/stderr - return "/tmp/gh-aw/agent-stdio.log" + return constants.AgentStdioLogPath } // GetRequiredSecretNames returns an empty list by default diff --git a/pkg/workflow/antigravity_engine.go b/pkg/workflow/antigravity_engine.go index 7510cb48b52..6532d365422 100644 --- a/pkg/workflow/antigravity_engine.go +++ b/pkg/workflow/antigravity_engine.go @@ -105,7 +105,7 @@ func (e *AntigravityEngine) GetInstallationSteps(workflowData *WorkflowData) []G // ancestor under /tmp/gh-aw/ and the actions/upload-artifact LCA calculation stays correct. func (e *AntigravityEngine) GetDeclaredOutputFiles() []string { return []string{ - "/tmp/gh-aw/antigravity-client-error-*.json", + constants.AntigravityClientErrorGlob, } } @@ -237,7 +237,7 @@ touch %s // Build environment variables env := map[string]string{ "ANTIGRAVITY_API_KEY": "${{ secrets.ANTIGRAVITY_API_KEY }}", - "GH_AW_PROMPT": "/tmp/gh-aw/aw-prompts/prompt.txt", + "GH_AW_PROMPT": constants.AgentPromptFilePath, // Tag the step as a GitHub AW agentic execution for discoverability by agents "GITHUB_AW": "true", "GITHUB_WORKSPACE": "${{ github.workspace }}", diff --git a/pkg/workflow/antigravity_logs.go b/pkg/workflow/antigravity_logs.go index eecb29ac5b2..7d2548a9a34 100644 --- a/pkg/workflow/antigravity_logs.go +++ b/pkg/workflow/antigravity_logs.go @@ -4,6 +4,7 @@ import ( "encoding/json" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" ) @@ -110,7 +111,7 @@ func (e *AntigravityEngine) GetLogParserScriptId() string { // GetLogFileForParsing returns the log file path for parsing func (e *AntigravityEngine) GetLogFileForParsing() string { - return "/tmp/gh-aw/agent-stdio.log" + return constants.AgentStdioLogPath } // GetDefaultDetectionModel returns the default model for threat detection diff --git a/pkg/workflow/antigravity_mcp.go b/pkg/workflow/antigravity_mcp.go index d37d7b02c9f..7bca6d703de 100644 --- a/pkg/workflow/antigravity_mcp.go +++ b/pkg/workflow/antigravity_mcp.go @@ -13,5 +13,5 @@ func (e *AntigravityEngine) RenderMCPConfig(yaml *strings.Builder, tools map[str antigravityMCPLog.Printf("Rendering MCP config for Antigravity: tool_count=%d, mcp_tool_count=%d", len(tools), len(mcpTools)) // Antigravity uses JSON format without Copilot-specific fields and multi-line args - return renderDefaultJSONMCPConfig(yaml, tools, mcpTools, workflowData, "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json") + return renderDefaultJSONMCPConfig(yaml, tools, mcpTools, workflowData, MCPConfigServersFilePathShell) } diff --git a/pkg/workflow/awf_helpers.go b/pkg/workflow/awf_helpers.go index f98e3312d46..994821251c0 100644 --- a/pkg/workflow/awf_helpers.go +++ b/pkg/workflow/awf_helpers.go @@ -231,7 +231,7 @@ fi`, // Build the expandable args string for args that need shell variable expansion. // These MUST be appended as raw (unescaped) strings because single-quoting would // prevent the runner's shell from expanding ${GITHUB_WORKSPACE} and ${RUNNER_TEMP}. - ghAwDir := "${RUNNER_TEMP}/gh-aw" + ghAwDir := constants.GhAwRootDirShell expandableArgs := fmt.Sprintf( `--container-workdir "${GITHUB_WORKSPACE}" --mount "%s:%s:ro" --mount "%s:/host%s:ro"`, ghAwDir, ghAwDir, ghAwDir, ghAwDir, @@ -317,7 +317,7 @@ fi`, // is mounted :ro above; this child mount overrides access for the staging subdirectory only. // The staging directory must already exist on the host (created in Generate Safe Outputs Config step). if config.WorkflowData != nil && config.WorkflowData.SafeOutputs != nil && config.WorkflowData.SafeOutputs.UploadArtifact != nil { - stagingDir := "${RUNNER_TEMP}/gh-aw/safeoutputs/upload-artifacts" + stagingDir := SafeOutputsUploadArtifactsDirShell expandableArgs += fmt.Sprintf(` --mount "%s:%s:rw"`, stagingDir, stagingDir) awfHelpersLog.Print("Added read-write mount for upload_artifact staging directory") } @@ -569,7 +569,7 @@ func BuildAWFArgs(config AWFCommandConfig) []string { if isGitHubCLIModeEnabled(config.WorkflowData) { if awfSupportsCliProxy(firewallConfig) { awfArgs = append(awfArgs, "--difc-proxy-host", "host.docker.internal:18443") - awfArgs = append(awfArgs, "--difc-proxy-ca-cert", "/tmp/gh-aw/difc-proxy-tls/ca.crt") + awfArgs = append(awfArgs, "--difc-proxy-ca-cert", constants.DifcProxyTLSCACertPath) awfHelpersLog.Print("Added --difc-proxy-host and --difc-proxy-ca-cert for CLI proxy sidecar") } else { awfHelpersLog.Printf("Skipping CLI proxy flags: AWF version %q is older than minimum %s", getAWFImageTag(firewallConfig), constants.AWFCliProxyMinVersion) diff --git a/pkg/workflow/claude_engine.go b/pkg/workflow/claude_engine.go index 3fe2b077334..fd12762e2e1 100644 --- a/pkg/workflow/claude_engine.go +++ b/pkg/workflow/claude_engine.go @@ -357,7 +357,7 @@ func (e *ClaudeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str // "Fast mode unavailable: Fast mode is not available in the Agent SDK", // which crashes the agent mid-session on every API call. "CLAUDE_CODE_DISABLE_FAST_MODE": "1", - "GH_AW_PROMPT": "/tmp/gh-aw/aw-prompts/prompt.txt", + "GH_AW_PROMPT": constants.AgentPromptFilePath, // Tag the step as a GitHub AW agentic execution for discoverability by agents "GITHUB_AW": "true", // Override GITHUB_STEP_SUMMARY with a path that exists inside the sandbox. @@ -384,7 +384,7 @@ func (e *ClaudeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str // Add GH_AW_MCP_CONFIG for MCP server configuration only if there are MCP servers if HasMCPServers(workflowData) { - env["GH_AW_MCP_CONFIG"] = "${{ runner.temp }}/gh-aw/mcp-config/mcp-servers.json" + env["GH_AW_MCP_CONFIG"] = MCPConfigServersFilePathGHAExpr } // In sandbox (AWF) mode, set git identity environment variables so the first git commit diff --git a/pkg/workflow/claude_mcp.go b/pkg/workflow/claude_mcp.go index 459a6cabf94..032ee87f809 100644 --- a/pkg/workflow/claude_mcp.go +++ b/pkg/workflow/claude_mcp.go @@ -13,5 +13,5 @@ func (e *ClaudeEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]a claudeMCPLog.Printf("Rendering MCP config for Claude: tool_count=%d, mcp_tool_count=%d", len(tools), len(mcpTools)) // Claude uses JSON format without Copilot-specific fields and multi-line args - return renderDefaultJSONMCPConfig(yaml, tools, mcpTools, workflowData, "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json") + return renderDefaultJSONMCPConfig(yaml, tools, mcpTools, workflowData, MCPConfigServersFilePathShell) } diff --git a/pkg/workflow/codex_engine.go b/pkg/workflow/codex_engine.go index 3a28a104de2..5f95d8b7ea9 100644 --- a/pkg/workflow/codex_engine.go +++ b/pkg/workflow/codex_engine.go @@ -132,7 +132,7 @@ func (e *CodexEngine) GetInstallationSteps(workflowData *WorkflowData) []GitHubA func (e *CodexEngine) GetDeclaredOutputFiles() []string { // Return the Codex log directory for artifact collection. return []string{ - "/tmp/gh-aw/mcp-config/logs/", + constants.MCPConfigLogsDir + "/", } } @@ -384,14 +384,14 @@ mkdir -p "$CODEX_HOME/logs" // we create this file before the agent starts and append it to the real // $GITHUB_STEP_SUMMARY after secret redaction. "GITHUB_STEP_SUMMARY": AgentStepSummaryPath, - "GH_AW_PROMPT": "/tmp/gh-aw/aw-prompts/prompt.txt", + "GH_AW_PROMPT": constants.AgentPromptFilePath, // Tag the step as a GitHub AW agentic execution for discoverability by agents "GITHUB_AW": "true", "RUNNER_TEMP": "${{ runner.temp }}", - "GH_AW_MCP_CONFIG": "${{ runner.temp }}/gh-aw/mcp-config/config.toml", + "GH_AW_MCP_CONFIG": MCPConfigTomlPathGHAExpr, // Keep Codex runtime state in /tmp/gh-aw because ${RUNNER_TEMP}/gh-aw is // mounted read-only inside the AWF chroot sandbox. - "CODEX_HOME": "/tmp/gh-aw/mcp-config", + "CODEX_HOME": constants.MCPConfigDir, // Enable verbose RUST_LOG only in debug mode (runner.debug == 1); default to warn to avoid noisy output. "RUST_LOG": "${{ runner.debug == 1 && 'trace,hyper_util=info,mio=info,reqwest=info,os_info=info,codex_otel=warn,codex_core=debug,ocodex_exec=debug' || 'warn' }}", "GH_AW_GITHUB_TOKEN": effectiveGitHubToken, diff --git a/pkg/workflow/codex_mcp.go b/pkg/workflow/codex_mcp.go index 1c124eaa173..73e461d5cb6 100644 --- a/pkg/workflow/codex_mcp.go +++ b/pkg/workflow/codex_mcp.go @@ -117,7 +117,7 @@ func (e *CodexEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]an tools: tools, mcpTools: mcpTools, workflowData: workflowData, - configPath: "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json", + configPath: MCPConfigServersFilePathShell, renderCustom: func(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool) error { return e.renderCodexJSONMCPConfigWithContext(yaml, toolName, toolConfig, isLast, workflowData) }, diff --git a/pkg/workflow/compiler_difc_proxy.go b/pkg/workflow/compiler_difc_proxy.go index 77ed48a0bca..e8e2c8a6cc2 100644 --- a/pkg/workflow/compiler_difc_proxy.go +++ b/pkg/workflow/compiler_difc_proxy.go @@ -320,7 +320,7 @@ func proxyEnvVars() map[string]string { "GH_REPO": "${{ github.repository }}", "GITHUB_API_URL": "https://localhost:18443/api/v3", "GITHUB_GRAPHQL_URL": "https://localhost:18443/api/graphql", - "NODE_EXTRA_CA_CERTS": "/tmp/gh-aw/proxy-logs/proxy-tls/ca.crt", + "NODE_EXTRA_CA_CERTS": constants.ProxyLogsTLSCACertPath, } } @@ -562,7 +562,7 @@ func difcProxyLogPaths(data *WorkflowData) []string { // Exclude proxy-tls/ to avoid uploading TLS material (mcp-logs/ is already // collected as part of standard MCP logging). return []string{ - "/tmp/gh-aw/proxy-logs/", + constants.ProxyLogsDir + "/", "!/tmp/gh-aw/proxy-logs/proxy-tls/", } } diff --git a/pkg/workflow/compiler_main_job.go b/pkg/workflow/compiler_main_job.go index 7061124e530..4bc0909dc54 100644 --- a/pkg/workflow/compiler_main_job.go +++ b/pkg/workflow/compiler_main_job.go @@ -291,7 +291,7 @@ func (c *Compiler) buildMainJob(data *WorkflowData, activationJobCreated bool) ( // Set GH_AW_MCP_LOG_DIR for safe outputs MCP server logging // Store in mcp-logs directory so it's included in mcp-logs artifact - env["GH_AW_MCP_LOG_DIR"] = "/tmp/gh-aw/mcp-logs/safeoutputs" + env["GH_AW_MCP_LOG_DIR"] = constants.MCPLogsSafeOutputsDir // Note: GH_AW_SAFE_OUTPUTS, GH_AW_SAFE_OUTPUTS_CONFIG_PATH, and // GH_AW_SAFE_OUTPUTS_TOOLS_PATH are set via a run step (see generateSetRuntimePathsStep) diff --git a/pkg/workflow/compiler_safe_outputs_job.go b/pkg/workflow/compiler_safe_outputs_job.go index 3937406e749..b110560ef40 100644 --- a/pkg/workflow/compiler_safe_outputs_job.go +++ b/pkg/workflow/compiler_safe_outputs_job.go @@ -118,7 +118,7 @@ func (c *Compiler) buildSafeOutputsSetupAndDownloadSteps(data *WorkflowData, age consolidatedSafeOutputsJobLog.Print("Adding patch artifact download for create-pull-request or push-to-pull-request-branch") patchDownloadSteps := buildArtifactDownloadSteps(ArtifactDownloadConfig{ ArtifactName: agentArtifactPrefix + constants.AgentArtifactName, - DownloadPath: "/tmp/gh-aw/", + DownloadPath: constants.TmpGhAwDir + "/", SetupEnvStep: false, // No environment variable needed, the script checks the file directly StepName: "Download patch artifact", }, c.getActionPin) @@ -478,7 +478,7 @@ func (c *Compiler) buildSafeOutputsJobFromParts( if usesPatchesAndCheckouts(data.SafeOutputs) { patchDownloadSteps := buildArtifactDownloadSteps(ArtifactDownloadConfig{ ArtifactName: agentArtifactPrefix + constants.AgentArtifactName, - DownloadPath: "/tmp/gh-aw/", + DownloadPath: constants.TmpGhAwDir + "/", SetupEnvStep: false, StepName: "Download patch artifact", }, c.getActionPin) diff --git a/pkg/workflow/compiler_yaml.go b/pkg/workflow/compiler_yaml.go index 96de1957523..41a23ea4ec9 100644 --- a/pkg/workflow/compiler_yaml.go +++ b/pkg/workflow/compiler_yaml.go @@ -669,7 +669,7 @@ func (c *Compiler) generatePrompt(yaml *strings.Builder, data *WorkflowData, pre // Extract everything from ".github/" onwards (inclusive) // +1 to skip the leading slash, so we get ".github/workflows/..." not "/.github/workflows/..." workflowFilePath = normalizedPath[githubIndex+1:] - } else if strings.HasPrefix(normalizedPath, ".github/") { + } else if strings.HasPrefix(normalizedPath, constants.DotGithubDir) { // Relative path already starting with ".github/" — use as-is. // This can happen when the compiler is invoked with a relative markdown path // (e.g. ".github/workflows/test.md") rather than an absolute one. @@ -1071,7 +1071,7 @@ func resolveWorkspaceRoot(markdownPath string) string { // Absolute or non-root-relative path: strip everything from "/.github/" onward. return filepath.FromSlash(before) } - if strings.HasPrefix(normalized, ".github/") { + if strings.HasPrefix(normalized, constants.DotGithubDir) { // Path already starts at the workspace root. return "." } diff --git a/pkg/workflow/compiler_yaml_main_job.go b/pkg/workflow/compiler_yaml_main_job.go index 6289cf5e50f..3d47db14adc 100644 --- a/pkg/workflow/compiler_yaml_main_job.go +++ b/pkg/workflow/compiler_yaml_main_job.go @@ -462,9 +462,9 @@ func (c *Compiler) generateEngineInstallAndPreAgentSteps(yaml *strings.Builder, func (c *Compiler) generateAgentRunSteps(yaml *strings.Builder, data *WorkflowData, engine CodingAgentEngine, needsGitConfig bool) ([]string, string, error) { // Collect artifact paths for unified upload at the end var artifactPaths []string - artifactPaths = append(artifactPaths, "/tmp/gh-aw/aw-prompts/prompt.txt") + artifactPaths = append(artifactPaths, constants.AgentPromptFilePath) - logFileFull := "/tmp/gh-aw/agent-stdio.log" + logFileFull := constants.AgentStdioLogPath // Clean credentials before executing the agentic engine. // This removes git credentials from .git/config and, when known credential-leaking @@ -581,21 +581,21 @@ func (c *Compiler) collectArtifactPaths(data *WorkflowData, engine CodingAgentEn paths = append(paths, getEngineArtifactPaths(engine)...) // Collect MCP logs. - paths = append(paths, "/tmp/gh-aw/mcp-logs/") + paths = append(paths, constants.MCPLogsDir+"/") // Collect DIFC proxy logs (proxy-tls certs + container stderr) when proxy was injected paths = append(paths, difcProxyLogPaths(data)...) // Collect MCPScripts logs path if mcp-scripts is enabled if IsMCPScriptsEnabled(data.MCPScripts) { - paths = append(paths, "/tmp/gh-aw/mcp-scripts/logs/") + paths = append(paths, constants.MCPScriptsLogsDir+"/") } // Include the aggregated agent_usage.json in the agent artifact so third-party // tools can consume structured token data without parsing the step summary. // Requires AWF v0.25.8+ if isFirewallEnabled(data) { - paths = append(paths, "/tmp/gh-aw/"+constants.TokenUsageFilename) + paths = append(paths, constants.TmpGhAwDir+"/"+constants.TokenUsageFilename) } // Collect agent stdio logs path for unified upload @@ -608,29 +608,29 @@ func (c *Compiler) collectArtifactPaths(data *WorkflowData, engine CodingAgentEn // Collect agent-generated files path for unified upload // This directory is used by workflows that instruct the agent to write files // (e.g., smoke-claude status summaries) - paths = append(paths, "/tmp/gh-aw/agent/") + paths = append(paths, constants.AgentDir+"/") // Collect GitHub API rate-limit log for observability. // Written by github_rate_limit_logger.cjs during REST API calls. - paths = append(paths, "/tmp/gh-aw/"+constants.GithubRateLimitsFilename) + paths = append(paths, constants.TmpGhAwDir+"/"+constants.GithubRateLimitsFilename) // Collect OTLP span mirror — enables post-hoc trace debugging without a live collector. // Written by send_otlp_span.cjs; each line is a full OTLP/HTTP JSON traces payload. // Only included when OTLP is configured for this workflow. if isOTLPEnabled(data) { - paths = append(paths, "/tmp/gh-aw/"+constants.OtelJsonlFilename) - paths = append(paths, "/tmp/gh-aw/"+constants.OtlpExportErrorsFilename) + paths = append(paths, constants.TmpGhAwDir+"/"+constants.OtelJsonlFilename) + paths = append(paths, constants.TmpGhAwDir+"/"+constants.OtlpExportErrorsFilename) } // Collect safe outputs and agent output paths for the unified artifact. // These were previously uploaded as separate safe-output and agent-output artifacts. if data.SafeOutputs != nil { // Raw safe-output NDJSON (copied to /tmp/gh-aw/ by generateOutputCollectionStep) - paths = append(paths, "/tmp/gh-aw/"+constants.SafeOutputsFilename) + paths = append(paths, constants.TmpGhAwDir+"/"+constants.SafeOutputsFilename) // Processed agent output JSON produced by collect_ndjson_output.cjs - paths = append(paths, "/tmp/gh-aw/"+constants.AgentOutputFilename) + paths = append(paths, constants.TmpGhAwDir+"/"+constants.AgentOutputFilename) if data.SafeOutputs.CommentMemory != nil { - paths = append(paths, "/tmp/gh-aw/comment-memory/") + paths = append(paths, constants.CommentMemoryDir+"/") } } @@ -644,13 +644,13 @@ func (c *Compiler) collectArtifactPaths(data *WorkflowData, engine CodingAgentEn // safe-output handler is staged and doesn't need checkout itself) threatDetectionNeedsPatches := IsDetectionJobEnabled(data.SafeOutputs) if usesPatchesAndCheckouts(data.SafeOutputs) || threatDetectionNeedsPatches { - paths = append(paths, "/tmp/gh-aw/aw-*.patch") + paths = append(paths, constants.AwPatchGlob) // Bundle files are generated when patch-format: bundle is configured. // Both formats use the same download path in the safe_outputs job, so // include the bundle glob unconditionally alongside the patch glob. // The artifact upload step already sets if-no-files-found: ignore, so // this is safe even when no bundle files exist. - paths = append(paths, "/tmp/gh-aw/aw-*.bundle") + paths = append(paths, constants.AwBundleGlob) } // Include firewall audit/observability logs in the unified agent artifact diff --git a/pkg/workflow/copilot_engine.go b/pkg/workflow/copilot_engine.go index 8ce6ccdbe55..2a8d5cea55d 100644 --- a/pkg/workflow/copilot_engine.go +++ b/pkg/workflow/copilot_engine.go @@ -137,7 +137,7 @@ func (e *CopilotEngine) GetAgentManifestFiles() []string { // The .github/ directory contains copilot-instructions.md, path-specific instruction // files, and copilot-setup-steps.yml — any of which can alter agent behaviour. func (e *CopilotEngine) GetAgentManifestPathPrefixes() []string { - return []string{".github/"} + return []string{constants.DotGithubDir} } // GetHarnessScriptName returns the filename of the JavaScript harness script that wraps diff --git a/pkg/workflow/copilot_engine_execution.go b/pkg/workflow/copilot_engine_execution.go index 28ba307f191..585120e0b2b 100644 --- a/pkg/workflow/copilot_engine_execution.go +++ b/pkg/workflow/copilot_engine_execution.go @@ -39,6 +39,10 @@ var copilotExecLog = logger.New("workflow:copilot_engine_execution") const customEngineCommandScriptPath = "/tmp/gh-aw/engine-command.sh" +// copilotAWFBinaryPath is the path to the Copilot CLI binary inside the AWF container. +// AWF installs the Copilot CLI binary at this path before executing the agent step. +const copilotAWFBinaryPath = "/usr/local/bin/copilot" + // copilotSettingsPath is the shell expression that resolves to the Copilot CLI settings // file at runtime. The Copilot CLI resolves its config directory as ~/.copilot, which is // /home/runner/.copilot on standard GitHub-hosted runners (HOME=/home/runner) but may @@ -142,7 +146,7 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st isBYOKMode := engineEnvHasKey(workflowData, constants.CopilotProviderBaseURL) if sandboxEnabled { // Simplified args for sandbox mode (AWF) - copilotArgs = []string{"--add-dir", "/tmp/gh-aw/", "--log-level", "all", "--log-dir", logsFolder} + copilotArgs = []string{"--add-dir", constants.TmpGhAwDir + "/", "--log-level", "all", "--log-dir", logsFolder} // Note: --add-dir "${GITHUB_WORKSPACE}" is appended raw after shellJoinArgs below // to allow shell variable expansion (cannot go through shellEscapeArg). @@ -151,7 +155,7 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st copilotExecLog.Print("Using firewall mode with simplified arguments") } else { // Original args for non-sandbox mode - copilotArgs = []string{"--add-dir", "/tmp/", "--add-dir", "/tmp/gh-aw/", "--add-dir", "/tmp/gh-aw/agent/", "--log-level", "all", "--log-dir", logsFolder} + copilotArgs = []string{"--add-dir", "/tmp/", "--add-dir", constants.TmpGhAwDir + "/", "--add-dir", constants.AgentDir + "/", "--log-level", "all", "--log-dir", logsFolder} copilotExecLog.Print("Using standard mode with full arguments") } @@ -262,7 +266,7 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st } else if sandboxEnabled { // AWF - use the installed binary directly // The binary is mounted into the AWF container from /usr/local/bin/copilot - commandName = "/usr/local/bin/copilot" + commandName = copilotAWFBinaryPath } else { // Non-sandbox mode: use standard copilot command commandName = "copilot" @@ -574,7 +578,7 @@ touch %s } // Always add GH_AW_PROMPT for agentic workflows - env["GH_AW_PROMPT"] = "/tmp/gh-aw/aw-prompts/prompt.txt" + env["GH_AW_PROMPT"] = constants.AgentPromptFilePath // Tag the step as a GitHub AW agentic execution for discoverability by agents env["GITHUB_AW"] = "true" diff --git a/pkg/workflow/copilot_logs.go b/pkg/workflow/copilot_logs.go index 5d57b489f27..5ce7eca80c6 100644 --- a/pkg/workflow/copilot_logs.go +++ b/pkg/workflow/copilot_logs.go @@ -4,6 +4,7 @@ import ( "encoding/json" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" ) @@ -462,5 +463,5 @@ func (e *CopilotEngine) GetErrorDetectionScriptId() string { // GetLogFileForParsing returns the log directory for Copilot CLI logs // Copilot writes detailed debug logs to /tmp/gh-aw/sandbox/agent/logs/ func (e *CopilotEngine) GetLogFileForParsing() string { - return "/tmp/gh-aw/sandbox/agent/logs/" + return constants.SandboxAgentLogsDir + "/" } diff --git a/pkg/workflow/crush_engine.go b/pkg/workflow/crush_engine.go index b3c5eb8b622..f5b7941023e 100644 --- a/pkg/workflow/crush_engine.go +++ b/pkg/workflow/crush_engine.go @@ -147,7 +147,7 @@ func (e *CrushEngine) GetExecutionSteps(workflowData *WorkflowData, logFile stri // Prompt from file (positional argument to `crush run`). // Keep this outside shellJoinArgs so command substitution expands at runtime. - promptArg := "\"$(cat /tmp/gh-aw/aw-prompts/prompt.txt)\"" + promptArg := "\"$(cat " + constants.AgentPromptFilePath + ")\"" // Build command name commandName := "crush" @@ -203,7 +203,7 @@ func (e *CrushEngine) GetExecutionSteps(workflowData *WorkflowData, logFile stri } env := map[string]string{ - "GH_AW_PROMPT": "/tmp/gh-aw/aw-prompts/prompt.txt", + "GH_AW_PROMPT": constants.AgentPromptFilePath, "GITHUB_WORKSPACE": "${{ github.workspace }}", "RUNNER_TEMP": "${{ runner.temp }}", "NO_PROXY": "localhost,127.0.0.1", diff --git a/pkg/workflow/crush_mcp.go b/pkg/workflow/crush_mcp.go index dcb15cc8d80..ee792d48df0 100644 --- a/pkg/workflow/crush_mcp.go +++ b/pkg/workflow/crush_mcp.go @@ -3,6 +3,7 @@ package workflow import ( "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" ) @@ -13,5 +14,5 @@ func (e *CrushEngine) RenderMCPConfig(sb *strings.Builder, tools map[string]any, crushMCPLog.Printf("Rendering MCP config for Crush: tool_count=%d, mcp_tool_count=%d", len(tools), len(mcpTools)) // Crush uses JSON format without Copilot-specific fields and multi-line args - return renderDefaultJSONMCPConfig(sb, tools, mcpTools, workflowData, "/tmp/gh-aw/mcp-config/mcp-servers.json") + return renderDefaultJSONMCPConfig(sb, tools, mcpTools, workflowData, constants.MCPConfigServersFilePath) } diff --git a/pkg/workflow/engine_output.go b/pkg/workflow/engine_output.go index af99f10cbda..01256d231fc 100644 --- a/pkg/workflow/engine_output.go +++ b/pkg/workflow/engine_output.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" ) @@ -33,7 +34,7 @@ func generateCleanupStep(outputFiles []string) (string, bool) { // Filter to get only workspace files (exclude /tmp/gh-aw/ files) var workspaceFiles []string for _, file := range outputFiles { - if !strings.HasPrefix(file, "/tmp/gh-aw/") { + if !strings.HasPrefix(file, constants.TmpGhAwDir+"/") { workspaceFiles = append(workspaceFiles, file) } } diff --git a/pkg/workflow/frontmatter_extraction_metadata.go b/pkg/workflow/frontmatter_extraction_metadata.go index 2fe7a6726aa..3bd57cef1d0 100644 --- a/pkg/workflow/frontmatter_extraction_metadata.go +++ b/pkg/workflow/frontmatter_extraction_metadata.go @@ -7,6 +7,7 @@ import ( "strconv" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" "github.com/github/gh-aw/pkg/typeutil" ) @@ -189,7 +190,7 @@ func buildLocalWorkflowSourceURL(markdownPath string) string { if idx != -1 { // Skip the leading slash to get ".github/workflows/...". relPath = normalised[idx+1:] - } else if strings.HasPrefix(normalised, ".github/") { + } else if strings.HasPrefix(normalised, constants.DotGithubDir) { // Already a relative path starting with ".github/". relPath = normalised } else { diff --git a/pkg/workflow/gemini_engine.go b/pkg/workflow/gemini_engine.go index df5af3b6ea9..03b2e8060e4 100644 --- a/pkg/workflow/gemini_engine.go +++ b/pkg/workflow/gemini_engine.go @@ -107,7 +107,7 @@ func (e *GeminiEngine) GetInstallationSteps(workflowData *WorkflowData) []GitHub // ancestor under /tmp/gh-aw/ and the actions/upload-artifact LCA calculation stays correct. func (e *GeminiEngine) GetDeclaredOutputFiles() []string { return []string{ - "/tmp/gh-aw/gemini-client-error-*.json", + constants.GeminiClientErrorGlob, } } @@ -193,7 +193,7 @@ func (e *GeminiEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str } // Append the prompt arg raw (not through shellJoinArgs) to preserve shell expansion - geminiCommand := fmt.Sprintf(`%s %s --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"`, commandName, shellJoinArgs(geminiArgs)) + geminiCommand := fmt.Sprintf(`%s %s --prompt "$(cat `+constants.AgentPromptFilePath+`)"`, commandName, shellJoinArgs(geminiArgs)) // Build the full command with AWF wrapping if enabled var command string @@ -249,7 +249,7 @@ touch %s // Build environment variables env := map[string]string{ "GEMINI_API_KEY": "${{ secrets.GEMINI_API_KEY }}", - "GH_AW_PROMPT": "/tmp/gh-aw/aw-prompts/prompt.txt", + "GH_AW_PROMPT": constants.AgentPromptFilePath, // Tag the step as a GitHub AW agentic execution for discoverability by agents "GITHUB_AW": "true", "GITHUB_WORKSPACE": "${{ github.workspace }}", diff --git a/pkg/workflow/gemini_logs.go b/pkg/workflow/gemini_logs.go index 636b9e9d99b..fb8a626d058 100644 --- a/pkg/workflow/gemini_logs.go +++ b/pkg/workflow/gemini_logs.go @@ -4,6 +4,7 @@ import ( "encoding/json" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" ) @@ -96,7 +97,7 @@ func (e *GeminiEngine) GetLogParserScriptId() string { // GetLogFileForParsing returns the log file path for parsing func (e *GeminiEngine) GetLogFileForParsing() string { - return "/tmp/gh-aw/agent-stdio.log" + return constants.AgentStdioLogPath } // GetDefaultDetectionModel returns the default model for threat detection diff --git a/pkg/workflow/gemini_mcp.go b/pkg/workflow/gemini_mcp.go index 444c51e78ff..a3a1ee08d35 100644 --- a/pkg/workflow/gemini_mcp.go +++ b/pkg/workflow/gemini_mcp.go @@ -13,5 +13,5 @@ func (e *GeminiEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]a geminiMCPLog.Printf("Rendering MCP config for Gemini: tool_count=%d, mcp_tool_count=%d", len(tools), len(mcpTools)) // Gemini uses JSON format without Copilot-specific fields and multi-line args - return renderDefaultJSONMCPConfig(yaml, tools, mcpTools, workflowData, "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json") + return renderDefaultJSONMCPConfig(yaml, tools, mcpTools, workflowData, MCPConfigServersFilePathShell) } diff --git a/pkg/workflow/mcp_config_playwright_renderer.go b/pkg/workflow/mcp_config_playwright_renderer.go index b68d5189303..b1ff582bf1b 100644 --- a/pkg/workflow/mcp_config_playwright_renderer.go +++ b/pkg/workflow/mcp_config_playwright_renderer.go @@ -61,6 +61,7 @@ import ( "encoding/json" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" ) @@ -137,7 +138,7 @@ func renderPlaywrightMCPConfigWithOptions(yaml *strings.Builder, playwrightConfi // creates a network namespace for renderer processes that cannot reach localhost. // This is required for screenshot workflows that serve docs on localhost. // Note: as of @playwright/mcp v0.0.26+, --no-sandbox is a direct top-level flag. - entrypointArgs := []string{"--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--no-sandbox"} + entrypointArgs := []string{"--output-dir", constants.MCPLogsPlaywrightDir, "--no-sandbox"} if len(customArgs) > 0 { entrypointArgs = append(entrypointArgs, customArgs...) } @@ -147,10 +148,10 @@ func renderPlaywrightMCPConfigWithOptions(yaml *strings.Builder, playwrightConfi // When guard policies follow, mounts is not the last field (add trailing comma) mcpPlaywrightLog.Printf("Adding volume mounts: guard_policies=%d", len(guardPolicies)) if len(guardPolicies) > 0 { - yaml.WriteString(" \"mounts\": [\"/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw\"],\n") + yaml.WriteString(" \"mounts\": [\"" + constants.MCPLogsDir + ":" + constants.MCPLogsDir + ":rw\"],\n") renderGuardPoliciesJSON(yaml, guardPolicies, " ") } else { - yaml.WriteString(" \"mounts\": [\"/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw\"]\n") + yaml.WriteString(" \"mounts\": [\"" + constants.MCPLogsDir + ":" + constants.MCPLogsDir + ":rw\"]\n") } // Note: tools field is NOT included here - the converter script adds it back diff --git a/pkg/workflow/mcp_environment.go b/pkg/workflow/mcp_environment.go index 54d4610f18e..308712feb91 100644 --- a/pkg/workflow/mcp_environment.go +++ b/pkg/workflow/mcp_environment.go @@ -203,7 +203,7 @@ func collectMCPEnvironmentVariables(tools map[string]any, mcpTools []string, wor // the converted MCP config can be copied into the writable Codex home directory. // This matches the value set on the agent step in codex_engine.go. if workflowData != nil && workflowData.AI == string(constants.CodexEngine) { - envVars["CODEX_HOME"] = "/tmp/gh-aw/mcp-config" + envVars["CODEX_HOME"] = constants.MCPConfigDir } return envVars diff --git a/pkg/workflow/mcp_renderer_builtin.go b/pkg/workflow/mcp_renderer_builtin.go index 8b13a166d5b..869723ff702 100644 --- a/pkg/workflow/mcp_renderer_builtin.go +++ b/pkg/workflow/mcp_renderer_builtin.go @@ -195,7 +195,7 @@ func (r *MCPConfigRendererUnified) renderAgenticWorkflowsTOML(yaml *strings.Buil mounts = []string{constants.DefaultWorkspaceMount, constants.DefaultTmpGhAwMount} } else { // Release mode: Use minimal Alpine image with mounted binaries - entrypoint = "${RUNNER_TEMP}/gh-aw/gh-aw" + entrypoint = GhAwBinaryPath entrypointArgs = []string{"mcp-server", "--validate-actor"} // Mount gh-aw binary, gh CLI binary, workspace, and temp directory mounts = []string{constants.DefaultGhAwMount, constants.DefaultGhBinaryMount, constants.DefaultWorkspaceMount, constants.DefaultTmpGhAwMount} @@ -338,7 +338,7 @@ func renderAgenticWorkflowsMCPConfigWithOptions(yaml *strings.Builder, isLast bo // Release mode: Use minimal Alpine image with mounted binaries // The gh-aw binary is mounted from ${RUNNER_TEMP}/gh-aw and executed directly // Pass --validate-actor flag to enable role-based access control - entrypoint = "${RUNNER_TEMP}/gh-aw/gh-aw" + entrypoint = GhAwBinaryPath entrypointArgs = []string{"mcp-server", "--validate-actor"} // Mount gh-aw binary, gh CLI binary, workspace, and temp directory mounts = []string{constants.DefaultGhAwMount, constants.DefaultGhBinaryMount, constants.DefaultWorkspaceMount, constants.DefaultTmpGhAwMount} diff --git a/pkg/workflow/opencode_engine.go b/pkg/workflow/opencode_engine.go index 46cfcd8a699..129f4ef81cd 100644 --- a/pkg/workflow/opencode_engine.go +++ b/pkg/workflow/opencode_engine.go @@ -107,7 +107,7 @@ func (e *OpenCodeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile s modelConfigured := workflowData.EngineConfig != nil && workflowData.EngineConfig.Model != "" openCodeArgs = append(openCodeArgs, "--print-logs", "--log-level", "DEBUG") - promptArg := "\"$(cat /tmp/gh-aw/aw-prompts/prompt.txt)\"" + promptArg := "\"$(cat " + constants.AgentPromptFilePath + ")\"" commandName := "opencode" if workflowData.EngineConfig != nil && workflowData.EngineConfig.Command != "" { @@ -160,7 +160,7 @@ func (e *OpenCodeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile s } env := map[string]string{ - "GH_AW_PROMPT": "/tmp/gh-aw/aw-prompts/prompt.txt", + "GH_AW_PROMPT": constants.AgentPromptFilePath, "GITHUB_WORKSPACE": "${{ github.workspace }}", "RUNNER_TEMP": "${{ runner.temp }}", "NO_PROXY": "localhost,127.0.0.1", diff --git a/pkg/workflow/opencode_mcp.go b/pkg/workflow/opencode_mcp.go index 169c9669635..fddd296fb01 100644 --- a/pkg/workflow/opencode_mcp.go +++ b/pkg/workflow/opencode_mcp.go @@ -3,6 +3,7 @@ package workflow import ( "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" ) @@ -12,5 +13,5 @@ var openCodeMCPLog = logger.New("workflow:opencode_mcp") func (e *OpenCodeEngine) RenderMCPConfig(sb *strings.Builder, tools map[string]any, mcpTools []string, workflowData *WorkflowData) error { openCodeMCPLog.Printf("Rendering MCP config for OpenCode: tool_count=%d, mcp_tool_count=%d", len(tools), len(mcpTools)) - return renderDefaultJSONMCPConfig(sb, tools, mcpTools, workflowData, "/tmp/gh-aw/mcp-config/mcp-servers.json") + return renderDefaultJSONMCPConfig(sb, tools, mcpTools, workflowData, constants.MCPConfigServersFilePath) } diff --git a/pkg/workflow/pi_engine.go b/pkg/workflow/pi_engine.go index d5ed86bf309..f0520cce9c3 100644 --- a/pkg/workflow/pi_engine.go +++ b/pkg/workflow/pi_engine.go @@ -298,7 +298,7 @@ func (e *PiEngine) GetExecutionSteps(workflowData *WorkflowData, logFile string) // further escaping in practice. modelsJSON := buildPiModelsJSON(profile.gatewayPort, profile.coreSecretNames[0], modelID) piModelsJSONSetup = fmt.Sprintf( - `mkdir -p /tmp/gh-aw/pi-agent-dir && printf '%%s\n' %s > /tmp/gh-aw/pi-agent-dir/models.json && `, + `mkdir -p `+constants.PiAgentDir+` && printf '%%s\n' %s > `+constants.PiAgentDir+`/models.json && `, shellEscapeArg(modelsJSON)) piArgs = append(piArgs, "--model", "aw-gateway/"+modelID) piLog.Printf("Pi: using models.json gateway routing for model %q via aw-gateway (port %d)", modelID, profile.gatewayPort) @@ -321,7 +321,7 @@ func (e *PiEngine) GetExecutionSteps(workflowData *WorkflowData, logFile string) // stdout (JSONL) and stderr are both piped through tee so that PiStreamingLogFile // captures all structured events while agent-stdio.log captures the same output. piCommand := fmt.Sprintf( - `cat /tmp/gh-aw/aw-prompts/prompt.txt | %s %s --extension "${RUNNER_TEMP}/gh-aw/actions/pi_provider.cjs" --extension "${RUNNER_TEMP}/gh-aw/actions/pi_steering_extension.cjs" 2>&1 | tee %s`, + "cat "+constants.AgentPromptFilePath+" | %s %s --extension \""+constants.GhAwRootDirShell+"/actions/pi_provider.cjs\" --extension \""+constants.GhAwRootDirShell+"/actions/pi_steering_extension.cjs\" 2>&1 | tee %s", commandName, shellJoinArgs(piArgs), PiStreamingLogFile) // Prepend models.json generation when the gateway-routing approach is used. @@ -379,7 +379,7 @@ touch %s // vars; routing is instead handled through models.json (firewall case) or by Pi's // native provider (no-firewall case). env := map[string]string{ - "GH_AW_PROMPT": "/tmp/gh-aw/aw-prompts/prompt.txt", + "GH_AW_PROMPT": constants.AgentPromptFilePath, "GITHUB_AW": "true", "GITHUB_WORKSPACE": "${{ github.workspace }}", "GITHUB_STEP_SUMMARY": AgentStepSummaryPath, @@ -404,7 +404,7 @@ touch %s // When the models.json gateway approach is used, tell Pi where to find it. if piModelsJSONSetup != "" { - env["PI_CODING_AGENT_DIR"] = "/tmp/gh-aw/pi-agent-dir" + env["PI_CODING_AGENT_DIR"] = constants.PiAgentDir piLog.Printf("Pi: setting PI_CODING_AGENT_DIR for models.json gateway config") } diff --git a/pkg/workflow/repo_memory.go b/pkg/workflow/repo_memory.go index c25d6eb5cdd..f2cc00099d3 100644 --- a/pkg/workflow/repo_memory.go +++ b/pkg/workflow/repo_memory.go @@ -489,7 +489,7 @@ func generateRepoMemoryArtifactUpload(builder *strings.Builder, data *WorkflowDa for _, memory := range data.RepoMemoryConfig.Memories { // Determine the memory directory - memoryDir := "/tmp/gh-aw/repo-memory/" + memory.ID + memoryDir := constants.RepoMemoryDir + "/" + memory.ID // Sanitize memory ID for artifact naming (remove hyphens, lowercase) sanitizedID := SanitizeWorkflowIDForCacheKey(memory.ID) @@ -548,7 +548,7 @@ func generateRepoMemorySteps(builder *strings.Builder, data *WorkflowData) { } // Determine the memory directory - memoryDir := "/tmp/gh-aw/repo-memory/" + memory.ID + memoryDir := constants.RepoMemoryDir + "/" + memory.ID // Step 1: Clone the repo-memory branch if memory.Wiki { @@ -661,7 +661,7 @@ func (c *Compiler) buildPushRepoMemoryJob(data *WorkflowData, threatDetectionEna step.WriteString(" continue-on-error: true\n") step.WriteString(" with:\n") fmt.Fprintf(&step, " name: %srepo-memory-%s\n", repoMemoryPrefix, sanitizedID) - fmt.Fprintf(&step, " path: /tmp/gh-aw/repo-memory/%s\n", memory.ID) + fmt.Fprintf(&step, " path: "+constants.RepoMemoryDir+"/%s\n", memory.ID) steps = append(steps, step.String()) } @@ -679,7 +679,7 @@ func (c *Compiler) buildPushRepoMemoryJob(data *WorkflowData, threatDetectionEna targetRepo = targetRepo + ".wiki" } - artifactDir := "/tmp/gh-aw/repo-memory/" + memory.ID + artifactDir := constants.RepoMemoryDir + "/" + memory.ID // Build file glob filter string fileGlobFilter := "" diff --git a/pkg/workflow/runtime_import_validation.go b/pkg/workflow/runtime_import_validation.go index c7480877622..ddee890cf22 100644 --- a/pkg/workflow/runtime_import_validation.go +++ b/pkg/workflow/runtime_import_validation.go @@ -23,6 +23,7 @@ import ( "regexp" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/parser" ) @@ -104,8 +105,8 @@ func validateRuntimeImportFiles(markdownContent string, workspaceDir string) ([] for _, filePath := range paths { // Normalize the path to be relative to .github folder normalizedPath := filePath - if strings.HasPrefix(normalizedPath, ".github/") { - normalizedPath = normalizedPath[8:] // Remove ".github/" + if strings.HasPrefix(normalizedPath, constants.DotGithubDir) { + normalizedPath = normalizedPath[len(constants.DotGithubDir):] // Remove ".github/" } else if strings.HasPrefix(normalizedPath, ".github\\") { normalizedPath = normalizedPath[8:] // Remove ".github\" (Windows) } diff --git a/pkg/workflow/safe_jobs.go b/pkg/workflow/safe_jobs.go index f4549111ced..ef7f5cefc4d 100644 --- a/pkg/workflow/safe_jobs.go +++ b/pkg/workflow/safe_jobs.go @@ -241,7 +241,7 @@ func (c *Compiler) buildSafeJobs(data *WorkflowData, threatDetectionEnabled bool agentArtifactPrefix := artifactPrefixExprForAgentDownstreamJob(data) downloadSteps := buildArtifactDownloadSteps(ArtifactDownloadConfig{ ArtifactName: agentArtifactPrefix + constants.AgentArtifactName, - DownloadPath: "${{ runner.temp }}/gh-aw/safe-jobs/", + DownloadPath: SafeJobsDirGHAExpr, SetupEnvStep: false, // We'll handle env vars separately to add job-specific ones StepName: "Download agent output artifact", }, c.getActionPin) @@ -255,7 +255,7 @@ func (c *Compiler) buildSafeJobs(data *WorkflowData, threatDetectionEnabled bool // GH_AW_AGENT_OUTPUT uses the runner.temp Actions expression so the path is // resolved by the runner without requiring a $GITHUB_OUTPUT write. setupEnvVars := map[string]string{ - "GH_AW_AGENT_OUTPUT": "${{ runner.temp }}/gh-aw/safe-jobs/" + constants.AgentOutputFilename, + "GH_AW_AGENT_OUTPUT": SafeJobsDirGHAExpr + constants.AgentOutputFilename, } // All job-specific env vars (literal or expression-based) are injected with // their original values. Nothing goes through $GITHUB_OUTPUT. diff --git a/pkg/workflow/safe_outputs_config_generation.go b/pkg/workflow/safe_outputs_config_generation.go index fb559eed388..6083949a043 100644 --- a/pkg/workflow/safe_outputs_config_generation.go +++ b/pkg/workflow/safe_outputs_config_generation.go @@ -6,6 +6,7 @@ import ( "sort" "strings" + "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/sliceutil" "github.com/github/gh-aw/pkg/stringutil" ) @@ -181,7 +182,7 @@ func generateSafeOutputsConfig(data *WorkflowData) (string, error) { for _, memory := range data.RepoMemoryConfig.Memories { memories = append(memories, map[string]any{ "id": memory.ID, - "dir": "/tmp/gh-aw/repo-memory/" + memory.ID, + "dir": constants.RepoMemoryDir + "/" + memory.ID, "max_file_size": memory.MaxFileSize, "max_patch_size": memory.MaxPatchSize, "max_file_count": memory.MaxFileCount, diff --git a/pkg/workflow/safe_outputs_steps.go b/pkg/workflow/safe_outputs_steps.go index 57b8abad0c3..cf51587de88 100644 --- a/pkg/workflow/safe_outputs_steps.go +++ b/pkg/workflow/safe_outputs_steps.go @@ -265,7 +265,7 @@ func buildAgentOutputDownloadSteps(prefix string, pinAction func(string) string) return buildArtifactDownloadSteps(ArtifactDownloadConfig{ ArtifactName: prefix + constants.AgentArtifactName, // Unified agent artifact (prefixed in workflow_call) ArtifactFilename: constants.AgentOutputFilename, // Filename inside the artifact directory - DownloadPath: "/tmp/gh-aw/", + DownloadPath: constants.TmpGhAwDir + "/", SetupEnvStep: true, EnvVarName: "GH_AW_AGENT_OUTPUT", StepName: "Download agent output artifact", diff --git a/pkg/workflow/setup_action_paths.go b/pkg/workflow/setup_action_paths.go index 620404ae299..c353e3bd394 100644 --- a/pkg/workflow/setup_action_paths.go +++ b/pkg/workflow/setup_action_paths.go @@ -24,5 +24,25 @@ const GhAwMCPScriptsDir = constants.GhAwRootDirShell + "/mcp-scripts" // GhAwBinaryPath is the path to the gh-aw binary on the runner const GhAwBinaryPath = constants.GhAwRootDirShell + "/gh-aw" -// SafeJobsDownloadDir is the directory for safe job files on the runner +// SafeJobsDownloadDir is the directory for safe job files on the runner (shell env var form). const SafeJobsDownloadDir = constants.GhAwRootDirShell + "/safe-jobs/" + +// SafeJobsDirGHAExpr is the same as SafeJobsDownloadDir but uses the GitHub Actions +// expression form for use in YAML `with:` fields. +const SafeJobsDirGHAExpr = constants.GhAwRootDir + "/safe-jobs/" + +// MCPConfigServersFilePathShell is the path to the MCP servers configuration file on the +// runner (shell env var form), for use inside `run:` blocks. +const MCPConfigServersFilePathShell = constants.GhAwRootDirShell + "/mcp-config/mcp-servers.json" + +// MCPConfigServersFilePathGHAExpr is the same as MCPConfigServersFilePathShell but uses the +// GitHub Actions expression form for use in YAML `with:` and `env:` fields. +const MCPConfigServersFilePathGHAExpr = constants.GhAwRootDir + "/mcp-config/mcp-servers.json" + +// MCPConfigTomlPathGHAExpr is the path to the Codex MCP configuration TOML file on the +// runner, in GitHub Actions expression form. +const MCPConfigTomlPathGHAExpr = constants.GhAwRootDir + "/mcp-config/config.toml" + +// SafeOutputsUploadArtifactsDirShell is the path to the safe-outputs upload-artifacts +// staging directory on the runner (shell env var form). +const SafeOutputsUploadArtifactsDirShell = SafeOutputsDirShell + "/upload-artifacts" diff --git a/pkg/workflow/step_order_validation.go b/pkg/workflow/step_order_validation.go index d47c36774a5..34f3a7e2c80 100644 --- a/pkg/workflow/step_order_validation.go +++ b/pkg/workflow/step_order_validation.go @@ -6,6 +6,8 @@ import ( "path/filepath" "slices" "strings" + + "github.com/github/gh-aw/pkg/constants" ) var stepOrderLog = newValidationLogger("step_order") @@ -188,7 +190,7 @@ func isPathScannedBySecretRedaction(path string) bool { // Accept both literal paths and environment variable references. // Engines that produce output outside /tmp/gh-aw/ must move their files into /tmp/gh-aw/ // via GetPreBundleSteps before the unified artifact upload (see gemini_engine.go). - if !strings.HasPrefix(path, "/tmp/gh-aw/") && !strings.HasPrefix(path, "${RUNNER_TEMP}/gh-aw/") && !strings.HasPrefix(path, "${{ runner.temp }}/gh-aw/") { + if !strings.HasPrefix(path, constants.TmpGhAwDir+"/") && !strings.HasPrefix(path, constants.GhAwRootDirShell+"/") && !strings.HasPrefix(path, constants.GhAwRootDir+"/") { // Check if it's an environment variable that might resolve to /tmp/gh-aw/ or ${RUNNER_TEMP}/gh-aw/ // For now, we'll allow ${{ env.* }} patterns through as we can't resolve them at compile time // Assume environment variables that might contain /tmp/gh-aw or ${RUNNER_TEMP}/gh-aw paths are safe diff --git a/pkg/workflow/threat_detection.go b/pkg/workflow/threat_detection.go index 3ae34607623..910ca24a51a 100644 --- a/pkg/workflow/threat_detection.go +++ b/pkg/workflow/threat_detection.go @@ -745,7 +745,7 @@ func (c *Compiler) buildDetectionEngineExecutionStep(data *WorkflowData) []strin } } - logFile := "/tmp/gh-aw/threat-detection/detection.log" + logFile := constants.ThreatDetectionLogPath executionSteps := engine.GetExecutionSteps(threatDetectionData, logFile) for _, step := range executionSteps { for i, line := range step { From 5b3b251f12d7a26bdc39a944b90c06474f6e3922 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 12 Jun 2026 14:19:48 +0000 Subject: [PATCH 4/4] fix: address review feedback on format string and proxy-tls path constant Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/constants/path_constants.go | 3 +++ pkg/workflow/compiler_difc_proxy.go | 2 +- pkg/workflow/repo_memory.go | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/constants/path_constants.go b/pkg/constants/path_constants.go index f28d713d651..8cdd4d7e9e1 100644 --- a/pkg/constants/path_constants.go +++ b/pkg/constants/path_constants.go @@ -87,6 +87,9 @@ const DifcProxyTLSCACertPath = "/tmp/gh-aw/difc-proxy-tls/ca.crt" // ProxyLogsDir is the directory for proxy logs, without a trailing slash. const ProxyLogsDir = "/tmp/gh-aw/proxy-logs" +// ProxyLogsTLSDir is the directory for proxy TLS certificates and keys, without a trailing slash. +const ProxyLogsTLSDir = "/tmp/gh-aw/proxy-logs/proxy-tls" + // ProxyLogsTLSCACertPath is the path to the proxy TLS CA certificate file. const ProxyLogsTLSCACertPath = "/tmp/gh-aw/proxy-logs/proxy-tls/ca.crt" diff --git a/pkg/workflow/compiler_difc_proxy.go b/pkg/workflow/compiler_difc_proxy.go index e8e2c8a6cc2..23cdbf795d7 100644 --- a/pkg/workflow/compiler_difc_proxy.go +++ b/pkg/workflow/compiler_difc_proxy.go @@ -563,6 +563,6 @@ func difcProxyLogPaths(data *WorkflowData) []string { // collected as part of standard MCP logging). return []string{ constants.ProxyLogsDir + "/", - "!/tmp/gh-aw/proxy-logs/proxy-tls/", + "!" + constants.ProxyLogsTLSDir + "/", } } diff --git a/pkg/workflow/repo_memory.go b/pkg/workflow/repo_memory.go index f2cc00099d3..e6fcbeac11b 100644 --- a/pkg/workflow/repo_memory.go +++ b/pkg/workflow/repo_memory.go @@ -661,7 +661,7 @@ func (c *Compiler) buildPushRepoMemoryJob(data *WorkflowData, threatDetectionEna step.WriteString(" continue-on-error: true\n") step.WriteString(" with:\n") fmt.Fprintf(&step, " name: %srepo-memory-%s\n", repoMemoryPrefix, sanitizedID) - fmt.Fprintf(&step, " path: "+constants.RepoMemoryDir+"/%s\n", memory.ID) + fmt.Fprintf(&step, " path: %s/%s\n", constants.RepoMemoryDir, memory.ID) steps = append(steps, step.String()) }