From ff9f93c9aae371a64872674b84b4d4efee9fa997 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 03:52:36 +0000 Subject: [PATCH 1/3] Initial plan From 44053538255fb58c2afc6c2a8d5697719c733d7a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 03:59:33 +0000 Subject: [PATCH 2/3] Initial exploration - understanding the init command structure Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/release.lock.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml index 42744f2d5a3..e5577baff77 100644 --- a/.github/workflows/release.lock.yml +++ b/.github/workflows/release.lock.yml @@ -5969,19 +5969,19 @@ jobs: - name: Download Go modules run: go mod download - name: Generate SBOM (SPDX format) - uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0 + uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10 with: artifact-name: sbom.spdx.json format: spdx-json output-file: sbom.spdx.json - name: Generate SBOM (CycloneDX format) - uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0 + uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10 with: artifact-name: sbom.cdx.json format: cyclonedx-json output-file: sbom.cdx.json - name: Upload SBOM artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 with: name: sbom-artifacts path: | From d5dd0a86496ffe39298f6a9e76a0c3e3ee8d297f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 04:04:54 +0000 Subject: [PATCH 3/3] Add .github/aw/logs/.gitignore creation to init command Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/git.go | 40 ++++++++++++++++++++++++++++++++++++ pkg/cli/init.go | 10 +++++++++ pkg/cli/init_command.go | 1 + pkg/cli/init_command_test.go | 6 ++++++ pkg/cli/init_test.go | 28 +++++++++++++++++++++++++ 5 files changed, 85 insertions(+) diff --git a/pkg/cli/git.go b/pkg/cli/git.go index 79967997a21..d7a7f80b683 100644 --- a/pkg/cli/git.go +++ b/pkg/cli/git.go @@ -113,6 +113,46 @@ func stageGitAttributesIfChanged() error { return exec.Command("git", "-C", gitRoot, "add", gitAttributesPath).Run() } +// ensureLogsGitignore ensures that .github/aw/logs/.gitignore exists to ignore log files +func ensureLogsGitignore() error { + gitLog.Print("Ensuring .github/aw/logs/.gitignore exists") + gitRoot, err := findGitRoot() + if err != nil { + return err // Not in a git repository, skip + } + + logsDir := filepath.Join(gitRoot, ".github", "aw", "logs") + gitignorePath := filepath.Join(logsDir, ".gitignore") + + // Check if .gitignore already exists + if _, err := os.Stat(gitignorePath); err == nil { + gitLog.Print(".github/aw/logs/.gitignore already exists") + return nil + } + + gitLog.Print("Creating .github/aw/logs directory and .gitignore") + // Create the logs directory if it doesn't exist + if err := os.MkdirAll(logsDir, 0755); err != nil { + gitLog.Printf("Failed to create logs directory: %v", err) + return fmt.Errorf("failed to create .github/aw/logs directory: %w", err) + } + + // Write the .gitignore file + gitignoreContent := `# Ignore all downloaded workflow logs +* + +# But keep the .gitignore file itself +!.gitignore +` + if err := os.WriteFile(gitignorePath, []byte(gitignoreContent), 0644); err != nil { + gitLog.Printf("Failed to write .gitignore: %v", err) + return fmt.Errorf("failed to write .github/aw/logs/.gitignore: %w", err) + } + + gitLog.Print("Successfully created .github/aw/logs/.gitignore") + return nil +} + // getCurrentBranch gets the current git branch name func getCurrentBranch() (string, error) { gitLog.Print("Getting current git branch") diff --git a/pkg/cli/init.go b/pkg/cli/init.go index 9dd937084d5..7d9b2b21679 100644 --- a/pkg/cli/init.go +++ b/pkg/cli/init.go @@ -32,6 +32,16 @@ func InitRepository(verbose bool, mcp bool) error { fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Configured .gitattributes")) } + // Ensure .github/aw/logs/.gitignore exists + initLog.Print("Ensuring .github/aw/logs/.gitignore exists") + if err := ensureLogsGitignore(); err != nil { + initLog.Printf("Failed to ensure logs .gitignore: %v", err) + return fmt.Errorf("failed to ensure logs .gitignore: %w", err) + } + if verbose { + fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Configured .github/aw/logs/.gitignore")) + } + // Write copilot instructions initLog.Print("Writing GitHub Copilot instructions") if err := ensureCopilotInstructions(verbose, false); err != nil { diff --git a/pkg/cli/init_command.go b/pkg/cli/init_command.go index fc344dc235a..ca0aedd49eb 100644 --- a/pkg/cli/init_command.go +++ b/pkg/cli/init_command.go @@ -17,6 +17,7 @@ func NewInitCommand() *cobra.Command { This command: - Configures .gitattributes to mark .lock.yml files as generated +- Creates .github/aw/logs/.gitignore to ignore downloaded workflow logs - Creates GitHub Copilot custom instructions at .github/aw/github-agentic-workflows.md - Creates the agent for workflow creation at .github/agents/create-agentic-workflow.agent.md - Creates the debug agentic workflow agent at .github/agents/debug-agentic-workflow.agent.md diff --git a/pkg/cli/init_command_test.go b/pkg/cli/init_command_test.go index 3b8c0f6b0e7..2a8c65c441e 100644 --- a/pkg/cli/init_command_test.go +++ b/pkg/cli/init_command_test.go @@ -109,6 +109,12 @@ func TestInitRepositoryBasic(t *testing.T) { if !strings.Contains(string(content), expectedEntry) { t.Errorf("Expected .gitattributes to contain %q", expectedEntry) } + + // Verify logs .gitignore was created + logsGitignorePath := filepath.Join(".github", "aw", "logs", ".gitignore") + if _, err := os.Stat(logsGitignorePath); os.IsNotExist(err) { + t.Error("Expected .github/aw/logs/.gitignore to be created") + } } func TestInitRepositoryWithMCP(t *testing.T) { diff --git a/pkg/cli/init_test.go b/pkg/cli/init_test.go index edd41be6e4e..b29eeb1b6d0 100644 --- a/pkg/cli/init_test.go +++ b/pkg/cli/init_test.go @@ -80,6 +80,28 @@ func TestInitRepository(t *testing.T) { t.Errorf("Expected copilot instructions file to exist") } + // Verify logs .gitignore was created + logsGitignorePath := filepath.Join(tempDir, ".github", "aw", "logs", ".gitignore") + if _, err := os.Stat(logsGitignorePath); os.IsNotExist(err) { + t.Errorf("Expected .github/aw/logs/.gitignore file to exist") + } + + // Verify logs .gitignore content + if content, err := os.ReadFile(logsGitignorePath); err == nil { + contentStr := string(content) + if !strings.Contains(contentStr, "# Ignore all downloaded workflow logs") { + t.Errorf("Expected .gitignore to contain comment about ignoring logs") + } + if !strings.Contains(contentStr, "*") { + t.Errorf("Expected .gitignore to contain wildcard pattern") + } + if !strings.Contains(contentStr, "!.gitignore") { + t.Errorf("Expected .gitignore to keep itself") + } + } else { + t.Errorf("Failed to read .github/aw/logs/.gitignore: %v", err) + } + // Verify agentic workflow agent was created agenticWorkflowAgentPath := filepath.Join(tempDir, ".github", "agents", "create-agentic-workflow.agent.md") if _, err := os.Stat(agenticWorkflowAgentPath); os.IsNotExist(err) { @@ -158,6 +180,12 @@ func TestInitRepository_Idempotent(t *testing.T) { if _, err := os.Stat(debugAgenticWorkflowAgentPath); os.IsNotExist(err) { t.Errorf("Expected debug agentic workflow agent file to exist after second call") } + + // Verify logs .gitignore still exists after second call + logsGitignorePath := filepath.Join(tempDir, ".github", "aw", "logs", ".gitignore") + if _, err := os.Stat(logsGitignorePath); os.IsNotExist(err) { + t.Errorf("Expected .github/aw/logs/.gitignore file to exist after second call") + } } func TestInitRepository_Verbose(t *testing.T) {