Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions .github/workflows/smoke-copilot-aoai-entra.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 13 additions & 12 deletions pkg/cli/add_package_manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -496,27 +497,27 @@ 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
}
}

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
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion pkg/cli/audit_comparison.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/experiments_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion pkg/cli/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
3 changes: 2 additions & 1 deletion pkg/cli/firewall_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"slices"
"strings"

"github.com/github/gh-aw/pkg/constants"
"github.com/github/gh-aw/pkg/logger"
)

Expand Down Expand Up @@ -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):
// <agentDir>/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
Expand Down
10 changes: 5 additions & 5 deletions pkg/cli/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/includes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/logs_run_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 4 additions & 3 deletions pkg/cli/mcp_tools_privileged.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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, ","))
}
Expand Down Expand Up @@ -525,7 +526,7 @@ Returns JSON describing the differences between the base run and each comparison
// Build: gh aw audit diff <base> <compare...> -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, ","))
}
Expand Down
22 changes: 17 additions & 5 deletions pkg/cli/shell_completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand All @@ -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")
}
Expand Down Expand Up @@ -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"))
}
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/trial_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading
Loading