Skip to content

Commit 742d36c

Browse files
authored
Extract hardcoded file paths to named constants (#39938)
1 parent 5d890d1 commit 742d36c

49 files changed

Lines changed: 288 additions & 99 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pkg/cli/git.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -313,14 +313,14 @@ func getRepositorySlugFromRemoteForPath(path string) string {
313313
func stageWorkflowChanges() {
314314
// Find git root and add .github/workflows relative to it
315315
if gitRoot, err := gitutil.FindGitRoot(); err == nil {
316-
workflowsPath := filepath.Join(gitRoot, ".github/workflows/")
316+
workflowsPath := filepath.Join(gitRoot, constants.WorkflowsDirSlash)
317317
_ = exec.Command("git", "-C", gitRoot, "add", workflowsPath).Run()
318318

319319
// Also stage .gitattributes if it was modified
320320
_ = stageGitAttributesIfChanged()
321321
} else {
322322
// Fallback to relative path if git root can't be found
323-
_ = exec.Command("git", "add", ".github/workflows/").Run()
323+
_ = exec.Command("git", "add", constants.WorkflowsDirSlash).Run()
324324
_ = exec.Command("git", "add", ".gitattributes").Run()
325325
}
326326
}
@@ -335,7 +335,7 @@ func ensureGitAttributes() (bool, error) {
335335
}
336336

337337
gitAttributesPath := filepath.Join(gitRoot, ".gitattributes")
338-
lockYmlEntry := ".github/workflows/*.lock.yml linguist-generated=true merge=ours"
338+
lockYmlEntry := constants.WorkflowsLockYmlGitAttributesEntry
339339
requiredEntries := []string{lockYmlEntry}
340340

341341
// Read existing .gitattributes file if it exists
@@ -357,7 +357,7 @@ func ensureGitAttributes() (bool, error) {
357357
break
358358
}
359359
// Check for old format entries that need updating
360-
if strings.HasPrefix(trimmedLine, ".github/workflows/*.lock.yml") && required == lockYmlEntry {
360+
if strings.HasPrefix(trimmedLine, constants.WorkflowsLockYmlGlob) && required == lockYmlEntry {
361361
gitLog.Print("Updating old .gitattributes entry format")
362362
lines[i] = lockYmlEntry
363363
found = true
@@ -414,7 +414,7 @@ func ensureLogsGitignore() error {
414414

415415
// Check if .gitignore already exists
416416
if _, err := os.Stat(gitignorePath); err == nil {
417-
gitLog.Print(".github/aw/logs/.gitignore already exists")
417+
gitLog.Print(".github/aw/logs/.gitignore already exists") //nolint:hardcodedfilepath
418418
return nil
419419
}
420420

pkg/cli/includes.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func FetchIncludeFromSource(includePath string, baseSpec *WorkflowSpec, verbose
9191
// If it's a relative path starting with shared/, it's relative to .github/
9292
var fullPath string
9393
if strings.HasPrefix(filePath, "shared/") {
94-
fullPath = ".github/" + filePath
94+
fullPath = constants.GithubDir + filePath
9595
} else {
9696
// Otherwise, resolve relative to the workflow path directory
9797
baseDir := getParentDir(baseSpec.WorkflowPath)

pkg/cli/logs_run_processor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ func inferWorkflowPathFromDisplayName(displayName string) string {
531531
if slug == "" {
532532
return ""
533533
}
534-
return ".github/workflows/" + slug + ".lock.yml"
534+
return constants.WorkflowsDirSlash + slug + ".lock.yml"
535535
}
536536

537537
// runHasDifcFilteredItems checks if a run's gateway logs contain any DIFC_FILTERED events.

pkg/cli/mcp_tools_privileged.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strconv"
1111
"strings"
1212

13+
"github.com/github/gh-aw/pkg/constants"
1314
"github.com/modelcontextprotocol/go-sdk/jsonrpc"
1415
"github.com/modelcontextprotocol/go-sdk/mcp"
1516
)
@@ -133,7 +134,7 @@ from where the previous request stopped due to timeout.`,
133134

134135
// Build command arguments
135136
// Force output directory to /tmp/gh-aw/aw-mcp/logs for MCP server
136-
cmdArgs := []string{"logs", "-o", "/tmp/gh-aw/aw-mcp/logs"}
137+
cmdArgs := []string{"logs", "-o", constants.TmpAwMcpLogsDir}
137138
if args.WorkflowName != "" {
138139
cmdArgs = append(cmdArgs, args.WorkflowName)
139140
}
@@ -384,7 +385,7 @@ Multi-run diff returns JSON describing changes between the base and each compari
384385
// Pass all run IDs/URLs directly - the audit command handles single vs. diff mode.
385386
cmdArgs := []string{"audit"}
386387
cmdArgs = append(cmdArgs, runItems...)
387-
cmdArgs = append(cmdArgs, "-o", "/tmp/gh-aw/aw-mcp/logs", "--json")
388+
cmdArgs = append(cmdArgs, "-o", constants.TmpAwMcpLogsDir, "--json")
388389
if len(args.Artifacts) > 0 {
389390
cmdArgs = append(cmdArgs, "--artifacts", strings.Join(args.Artifacts, ","))
390391
}
@@ -525,7 +526,7 @@ Returns JSON describing the differences between the base run and each comparison
525526
// Build: gh aw audit diff <base> <compare...> -o ... --json [--artifacts ...]
526527
cmdArgs := []string{"audit", "diff", args.BaseRunID}
527528
cmdArgs = append(cmdArgs, args.CompareRunIDs...)
528-
cmdArgs = append(cmdArgs, "-o", "/tmp/gh-aw/aw-mcp/logs", "--json")
529+
cmdArgs = append(cmdArgs, "-o", constants.TmpAwMcpLogsDir, "--json")
529530
if len(args.Artifacts) > 0 {
530531
cmdArgs = append(cmdArgs, "--artifacts", strings.Join(args.Artifacts, ","))
531532
}

pkg/cli/shell_completion.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func installBashCompletion(verbose bool, cmd *cobra.Command) error {
145145
brewPrefix := os.Getenv("HOMEBREW_PREFIX")
146146
if brewPrefix == "" {
147147
// Try common locations
148-
for _, prefix := range []string{"/opt/homebrew", "/usr/local"} {
148+
for _, prefix := range []string{constants.HomebrewPrefix, constants.UsrLocalPrefix} {
149149
if _, err := os.Stat(filepath.Join(prefix, "etc", "bash_completion.d")); err == nil {
150150
brewPrefix = prefix
151151
break
@@ -159,8 +159,8 @@ func installBashCompletion(verbose bool, cmd *cobra.Command) error {
159159
}
160160
} else {
161161
// Linux
162-
if _, err := os.Stat("/etc/bash_completion.d"); err == nil {
163-
completionPath = "/etc/bash_completion.d/gh-aw"
162+
if _, err := os.Stat(constants.BashCompletionDir); err == nil {
163+
completionPath = constants.BashCompletionGhAwPath
164164
} else {
165165
completionPath = filepath.Join(homeDir, ".bash_completion.d", "gh-aw")
166166
}
@@ -426,7 +426,7 @@ func uninstallBashCompletion(verbose bool) error {
426426
if runtime.GOOS == "darwin" {
427427
brewPrefix := os.Getenv("HOMEBREW_PREFIX")
428428
if brewPrefix == "" {
429-
for _, prefix := range []string{"/opt/homebrew", "/usr/local"} {
429+
for _, prefix := range []string{constants.HomebrewPrefix, constants.UsrLocalPrefix} {
430430
if _, err := os.Stat(filepath.Join(prefix, "etc", "bash_completion.d")); err == nil {
431431
possiblePaths = append(possiblePaths, filepath.Join(prefix, "etc", "bash_completion.d", "gh-aw"))
432432
}
@@ -438,7 +438,7 @@ func uninstallBashCompletion(verbose bool) error {
438438

439439
// System-wide installations (Linux)
440440
if runtime.GOOS == "linux" {
441-
possiblePaths = append(possiblePaths, "/etc/bash_completion.d/gh-aw")
441+
possiblePaths = append(possiblePaths, constants.BashCompletionGhAwPath)
442442
}
443443

444444
removed := false

pkg/cli/trial_repository.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ func installWorkflowInTrialMode(ctx context.Context, tempDir string, parsedSpec
312312

313313
// Compile the workflow with trial modifications
314314
config := CompileConfig{
315-
MarkdownFiles: []string{".github/workflows/" + parsedSpec.WorkflowName + ".md"},
315+
MarkdownFiles: []string{constants.WorkflowsDirSlash + parsedSpec.WorkflowName + ".md"},
316316
Verbose: opts.Verbose,
317317
EngineOverride: opts.EngineOverride,
318318
Validate: true,

pkg/constants/constants.go

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,177 @@ var SharedWorkflowForbiddenFields = []string{
364364
"tracker-id", // Tracker ID
365365
}
366366

367+
// Repository directory path constants
368+
//
369+
// These constants define the conventional repository-relative directory paths
370+
// used by gh-aw for GitHub Actions workflows, agents, and related configuration.
371+
372+
// GithubDir is the root .github directory prefix (with trailing slash).
373+
// Use this for path prefix comparisons against workspace-relative paths.
374+
const GithubDir = ".github/"
375+
376+
// WorkflowsDir is the GitHub Actions workflow directory path (without trailing slash).
377+
// This is the canonical location for workflow markdown and compiled lock YAML files.
378+
const WorkflowsDir = ".github/workflows"
379+
380+
// WorkflowsDirSlash is WorkflowsDir with a trailing slash.
381+
// Use this for path prefix matching (e.g. strings.HasPrefix or strings.Contains).
382+
const WorkflowsDirSlash = WorkflowsDir + "/"
383+
384+
// AgentsDir is the custom GitHub Copilot agent definitions directory (with trailing slash).
385+
const AgentsDir = ".github/agents/"
386+
387+
// WorkflowsLockYmlGlob is the glob pattern for compiled workflow lock YAML files.
388+
const WorkflowsLockYmlGlob = WorkflowsDirSlash + "*.lock.yml"
389+
390+
// WorkflowsLockYmlGitAttributesEntry is the .gitattributes entry that marks lock YAML
391+
// files as generated and sets the merge strategy.
392+
const WorkflowsLockYmlGitAttributesEntry = WorkflowsLockYmlGlob + " linguist-generated=true merge=ours"
393+
394+
// Temporary runtime directory constants (/tmp/gh-aw tree)
395+
//
396+
// These constants define the /tmp/gh-aw directory layout used by the agent
397+
// and engine harnesses during workflow execution. Paths here are always
398+
// in the /tmp/gh-aw tree regardless of whether the runner uses RUNNER_TEMP.
399+
// See also GhAwRootDir / GhAwRootDirShell for the host-side RUNNER_TEMP paths.
400+
401+
// TmpGhAwDir is the root /tmp/gh-aw directory (without trailing slash).
402+
const TmpGhAwDir = "/tmp/gh-aw"
403+
404+
// TmpGhAwDirSlash is TmpGhAwDir with a trailing slash.
405+
// Use for path prefix comparisons (e.g. strings.HasPrefix).
406+
const TmpGhAwDirSlash = TmpGhAwDir + "/"
407+
408+
// TmpGhAwAgentDir is the agent working directory in the /tmp/gh-aw tree.
409+
const TmpGhAwAgentDir = TmpGhAwDir + "/agent/"
410+
411+
// AgentStdioLogPath is the path for capturing agent standard I/O log output.
412+
const AgentStdioLogPath = TmpGhAwDir + "/agent-stdio.log"
413+
414+
// AwPromptsFile is the runtime prompt file path populated by the setup action.
415+
// Engine harnesses read this file to pass the compiled prompt to the AI engine.
416+
const AwPromptsFile = TmpGhAwDir + "/aw-prompts/prompt.txt"
417+
418+
// TmpMcpConfigDir is the mcp-config directory in the /tmp/gh-aw tree.
419+
// Engines that require a writable MCP config directory (e.g. Codex) use this path.
420+
const TmpMcpConfigDir = TmpGhAwDir + "/mcp-config"
421+
422+
// TmpMcpServersJsonPath is the MCP servers JSON config file in the /tmp tree.
423+
// Used by engines that resolve the config through the writable /tmp path.
424+
const TmpMcpServersJsonPath = TmpMcpConfigDir + "/mcp-servers.json"
425+
426+
// TmpMcpConfigLogsDir is the MCP config server log directory.
427+
const TmpMcpConfigLogsDir = TmpMcpConfigDir + "/logs/"
428+
429+
// TmpMcpLogsDir is the MCP server logs root directory (with trailing slash).
430+
const TmpMcpLogsDir = TmpGhAwDir + "/mcp-logs/"
431+
432+
// TmpMcpLogsSafeOutputsDir is the safe-outputs MCP server log directory.
433+
const TmpMcpLogsSafeOutputsDir = TmpGhAwDir + "/mcp-logs/safeoutputs"
434+
435+
// TmpMcpLogsPlaywrightDir is the Playwright MCP server log directory.
436+
const TmpMcpLogsPlaywrightDir = TmpGhAwDir + "/mcp-logs/playwright"
437+
438+
// TmpMcpLogsMount is the Docker volume mount spec for the MCP logs directory.
439+
const TmpMcpLogsMount = TmpGhAwDir + "/mcp-logs:" + TmpGhAwDir + "/mcp-logs"
440+
441+
// TmpMcpScriptsLogsDir is the mcp-scripts server log directory (with trailing slash).
442+
const TmpMcpScriptsLogsDir = TmpGhAwDir + "/mcp-scripts/logs/"
443+
444+
// TmpRepoMemoryDir is the repo-memory data directory (with trailing slash).
445+
const TmpRepoMemoryDir = TmpGhAwDir + "/repo-memory/"
446+
447+
// TmpCommentMemoryDir is the comment-memory data directory (with trailing slash).
448+
const TmpCommentMemoryDir = TmpGhAwDir + "/comment-memory/"
449+
450+
// TmpAwBundleGlob is the glob pattern for bundle files produced by the agent.
451+
const TmpAwBundleGlob = TmpGhAwDir + "/aw-*.bundle"
452+
453+
// TmpAwPatchGlob is the glob pattern for patch files produced by the agent.
454+
const TmpAwPatchGlob = TmpGhAwDir + "/aw-*.patch"
455+
456+
// TmpGeminiClientErrorGlob is the glob for Gemini client error JSON diagnostic files.
457+
const TmpGeminiClientErrorGlob = TmpGhAwDir + "/gemini-client-error-*.json"
458+
459+
// TmpAntigravityClientErrorGlob is the glob for Antigravity client error JSON diagnostic files.
460+
const TmpAntigravityClientErrorGlob = TmpGhAwDir + "/antigravity-client-error-*.json"
461+
462+
// TmpPiAgentDir is the Pi engine agent working directory.
463+
const TmpPiAgentDir = TmpGhAwDir + "/pi-agent-dir"
464+
465+
// ThreatDetectionLogPath is the threat detection engine log file path.
466+
const ThreatDetectionLogPath = TmpGhAwDir + "/threat-detection/detection.log"
467+
468+
// TmpProxyLogsDir is the DIFC proxy logs directory (with trailing slash).
469+
const TmpProxyLogsDir = TmpGhAwDir + "/proxy-logs/"
470+
471+
// TmpProxyTLSDir is the proxy TLS certificates sub-directory (with trailing slash).
472+
const TmpProxyTLSDir = TmpGhAwDir + "/proxy-logs/proxy-tls/"
473+
474+
// TmpProxyTLSCACert is the proxy TLS CA certificate file path.
475+
const TmpProxyTLSCACert = TmpGhAwDir + "/proxy-logs/proxy-tls/ca.crt"
476+
477+
// TmpDIFCProxyTLSCACert is the DIFC proxy TLS CA certificate file path.
478+
const TmpDIFCProxyTLSCACert = TmpGhAwDir + "/difc-proxy-tls/ca.crt"
479+
480+
// TmpAwMcpLogsDir is the aw-mcp server logs directory.
481+
const TmpAwMcpLogsDir = TmpGhAwDir + "/aw-mcp/logs"
482+
483+
// TmpSandboxAgentLogsDir is the sandbox agent logs directory (with trailing slash).
484+
const TmpSandboxAgentLogsDir = TmpGhAwDir + "/sandbox/agent/logs/"
485+
486+
// Shell and Actions expression form path constants
487+
//
488+
// These complement GhAwRootDirShell and GhAwRootDir for sub-paths commonly
489+
// referenced in both shell run: blocks and GitHub Actions expression contexts.
490+
491+
// GhAwRootDirShellSlash is GhAwRootDirShell with a trailing slash.
492+
// Use for path prefix matching in shell expressions (e.g. ${RUNNER_TEMP}/gh-aw/).
493+
const GhAwRootDirShellSlash = GhAwRootDirShell + "/"
494+
495+
// ShellMcpConfigDir is the mcp-config directory in shell environment variable form.
496+
const ShellMcpConfigDir = GhAwRootDirShell + "/mcp-config"
497+
498+
// ShellMcpServersJsonPath is the MCP servers JSON config file path in shell form.
499+
// Used by engines that resolve the config via the host RUNNER_TEMP path.
500+
const ShellMcpServersJsonPath = GhAwRootDirShell + "/mcp-config/mcp-servers.json"
501+
502+
// GhAwRootDirSlash is GhAwRootDir with a trailing slash (Actions expression form).
503+
const GhAwRootDirSlash = GhAwRootDir + "/"
504+
505+
// McpServersJsonPathExpr is the MCP servers JSON config path in Actions expression form.
506+
const McpServersJsonPathExpr = GhAwRootDir + "/mcp-config/mcp-servers.json"
507+
508+
// CodexMcpConfigTomlPath is the Codex MCP config TOML file path in Actions expression form.
509+
const CodexMcpConfigTomlPath = GhAwRootDir + "/mcp-config/config.toml"
510+
511+
// System path constants
512+
//
513+
// Well-known host system paths used by CLI tools and shell completion.
514+
515+
// CopilotBinaryPath is the path to the Copilot CLI binary inside AWF containers.
516+
const CopilotBinaryPath = "/usr/local/bin/copilot"
517+
518+
// BashCompletionDir is the system-wide bash completion directory.
519+
const BashCompletionDir = "/etc/bash_completion.d"
520+
521+
// BashCompletionGhAwPath is the gh-aw bash completion file path.
522+
const BashCompletionGhAwPath = BashCompletionDir + "/gh-aw"
523+
524+
// HomebrewPrefix is the default Homebrew installation prefix on macOS.
525+
const HomebrewPrefix = "/opt/homebrew"
526+
527+
// UsrLocalPrefix is the standard /usr/local installation prefix.
528+
const UsrLocalPrefix = "/usr/local"
529+
367530
// GetWorkflowDir returns the workflows directory path.
368531
// Always uses forward slashes, which are required for git/GitHub paths.
369532
// GH_AW_WORKFLOWS_DIR overrides the default; any OS-specific separators are normalized.
370533
func GetWorkflowDir() string {
371534
if dir := os.Getenv("GH_AW_WORKFLOWS_DIR"); dir != "" {
372535
return filepath.ToSlash(dir)
373536
}
374-
return ".github/workflows"
537+
return WorkflowsDir
375538
}
376539

377540
// MaxSymlinkDepth limits recursive symlink resolution when fetching remote files.

pkg/linters/hardcodedfilepath/hardcodedfilepath.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ func (r constRef) String() string {
5454
// ".github", or "${RUNNER_TEMP}" alone are not flagged.
5555
var pathPrefixes = []string{
5656
"/tmp/",
57-
"${RUNNER_TEMP}/",
58-
"${{ runner.temp }}/",
59-
".github/",
57+
"${RUNNER_TEMP}/", //nolint:hardcodedfilepath
58+
"${{ runner.temp }}/", //nolint:hardcodedfilepath
59+
".github/", //nolint:hardcodedfilepath
6060
"/opt/",
6161
"/usr/",
6262
"/var/",

pkg/parser/import_field_extractor.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"regexp"
1313
"strings"
1414

15+
"github.com/github/gh-aw/pkg/constants"
1516
"github.com/github/gh-aw/pkg/importinpututil"
1617
)
1718

@@ -960,7 +961,7 @@ func computeImportRelPath(fullPath, importPath string) string {
960961
if idx := strings.LastIndex(normalizedFullPath, "/.github/"); idx >= 0 {
961962
return normalizedFullPath[idx+1:] // +1 to skip the leading slash
962963
}
963-
if strings.HasPrefix(normalizedFullPath, ".github/") {
964+
if strings.HasPrefix(normalizedFullPath, constants.GithubDir) {
964965
return normalizedFullPath
965966
}
966967
return importPath

pkg/parser/include_expander.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"regexp"
99
"strings"
1010

11+
"github.com/github/gh-aw/pkg/constants"
1112
"github.com/github/gh-aw/pkg/logger"
1213
)
1314

@@ -192,7 +193,7 @@ func ExtractBodyLevelImportPaths(content, baseDir string) []BodyLevelImport {
192193
// Convert relative paths to workspace-root-relative.
193194
// Paths already starting with ".github/" are workspace-root-relative.
194195
// Absolute paths are used as-is.
195-
if !strings.HasPrefix(importPath, ".github/") && !filepath.IsAbs(importPath) {
196+
if !strings.HasPrefix(importPath, constants.GithubDir) && !filepath.IsAbs(importPath) {
196197
if repoRoot != "" {
197198
fullPath := filepath.Join(baseDir, importPath)
198199
if rel, err := filepath.Rel(repoRoot, fullPath); err == nil && !strings.HasPrefix(rel, "..") {

0 commit comments

Comments
 (0)