From 75908ea063c9e1b54e12bf29d538ef355188aea8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 22:15:21 +0000 Subject: [PATCH 1/4] Initial plan From d0c56eeaf1c0dab2d9762737bba551476b2f440d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 22:31:28 +0000 Subject: [PATCH 2/4] Add check-existing-pr shared workflow and integrate with cli-version-checker Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../workflows/cli-version-checker.lock.yml | 163 ++++++++++++------ .github/workflows/cli-version-checker.md | 11 ++ .github/workflows/shared/check-existing-pr.md | 105 +++++++++++ 3 files changed, 226 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/shared/check-existing-pr.md diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 47b188a8743..21c834bc8be 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -6,6 +6,7 @@ # Resolved workflow manifest: # Imports: # - shared/jqschema.md +# - shared/check-existing-pr.md # # Job Dependency Graph: # ```mermaid @@ -1155,6 +1156,104 @@ jobs: # Now you know which fields exist and can use them in your analysis ``` + ## Check for Existing Pull Request + + Before creating a new pull request, verify if there's already an open PR from this workflow to avoid duplicates. + + ### Search Strategy + + Use both title prefix and labels for highly effective PR matching: + + 1. **Search by title prefix and labels**: Use `search_pull_requests` with a query combining: + - Repository scope: `repo:${{ github.repository }}` + - PR filter: `is:pr` + - State filter: `is:open` + - Title pattern matching the workflow's title prefix + - Label filter matching the workflow's automation labels + + 2. **Fallback search**: If needed, use `list_pull_requests` with `state: open` and manually filter by: + - Title prefix match + - Label match (both "automation" and workflow-specific labels) + + ### Example Search Pattern + + For a workflow that creates PRs with: + - Title prefix: `[ca]` (CLI automation) + - Labels: `automation`, `dependencies` + + Use this search query: + ``` + repo:${{ github.repository }} is:pr is:open "[ca]" label:automation label:dependencies + ``` + + This pattern is highly effective because it combines: + - **Exact repository scoping**: Limits search to current repository + - **PR type filtering**: Only pull requests, not issues + - **State filtering**: Only open PRs + - **Title prefix matching**: Matches the unique workflow prefix in quotes for exact matching + - **Label combination**: Requires BOTH automation and workflow-specific labels + + ### Implementation + + When checking for existing PRs: + + 1. **First attempt** - Use `search_pull_requests`: + ``` + query: 'repo:${{ github.repository }} is:pr is:open "[your-prefix]" label:automation label:workflow-label' + ``` + + 2. **Verify results**: Check if any PRs are returned + - If found: Extract PR number, URL, and branch name + - If none: Proceed with creating a new PR + + 3. **Handle edge cases**: + - Multiple matching PRs: Use the most recently created/updated + - PR closed recently: Confirm it's truly open before reporting + + ### Stop Condition + + If an existing open PR is found: + - Print a clear message: "An open pull request already exists for this workflow" + - Include PR details: number, URL, title + - Stop execution immediately without creating a new PR + - Exit successfully (not as an error) + + ### Integration with Workflows + + Import this shared workflow in your agentic workflow frontmatter: + ```yaml + imports: + - shared/check-existing-pr.md + ``` + + Then, in your workflow instructions, add as the first step: + ``` + ### 0. Check for Existing Pull Request + Before starting any work, check if there's already an open pull request from this workflow: + - Use the search pattern with title prefix "[your-prefix]" and labels + - If found, print message and stop execution + - If not found, proceed with workflow tasks + ``` + + ### Search Query Examples + + **For CLI version checker** (`[ca]` prefix, `automation` + `dependencies` labels): + ``` + repo:${{ github.repository }} is:pr is:open "[ca]" label:automation label:dependencies + ``` + + **For tidy workflow** (`[tidy]` prefix, `automation` + `maintenance` labels): + ``` + repo:${{ github.repository }} is:pr is:open "[tidy]" label:automation label:maintenance + ``` + + **For security fixes** (`[security-fix]` prefix, `security` + `automated-fix` labels): + ``` + repo:${{ github.repository }} is:pr is:open "[security-fix]" label:security label:automated-fix + ``` + + This approach ensures extremely effective PR detection by requiring all criteria to match, making false positives virtually impossible. + # CLI Version Checker Monitor and update agentic CLI tools: Claude Code, GitHub Copilot CLI, OpenAI Codex, and GitHub MCP Server. @@ -1163,6 +1262,16 @@ jobs: ## Process + ### 0. Check for Existing Pull Request + Before starting version checks and updates, verify if there's already an open PR from this workflow: + - Use `search_pull_requests` with query: `repo:${{ github.repository }} is:pr is:open "[ca]" label:automation label:dependencies` + - If an existing open PR is found: + - Print message: "An open pull request already exists for CLI version updates" + - Include PR number, URL, and title + - Stop execution immediately + - If no existing PR found, proceed with version checks + + ### 1. Check for Updates For each CLI/MCP server: 1. Fetch latest version from NPM registry or GitHub releases 2. Compare with current version in `./pkg/constants/constants.go` @@ -1504,65 +1613,13 @@ jobs: # - TodoWrite # - WebFetch # - Write - # - mcp__github__download_workflow_run_artifact - # - mcp__github__get_code_scanning_alert - # - mcp__github__get_commit - # - mcp__github__get_dependabot_alert - # - mcp__github__get_discussion - # - mcp__github__get_discussion_comments - # - mcp__github__get_file_contents - # - mcp__github__get_issue - # - mcp__github__get_issue_comments - # - mcp__github__get_job_logs - # - mcp__github__get_label - # - mcp__github__get_latest_release - # - mcp__github__get_me - # - mcp__github__get_notification_details - # - mcp__github__get_pull_request - # - mcp__github__get_pull_request_comments - # - mcp__github__get_pull_request_diff - # - mcp__github__get_pull_request_files - # - mcp__github__get_pull_request_review_comments - # - mcp__github__get_pull_request_reviews - # - mcp__github__get_pull_request_status - # - mcp__github__get_release_by_tag - # - mcp__github__get_secret_scanning_alert - # - mcp__github__get_tag - # - mcp__github__get_workflow_run - # - mcp__github__get_workflow_run_logs - # - mcp__github__get_workflow_run_usage - # - mcp__github__list_branches - # - mcp__github__list_code_scanning_alerts - # - mcp__github__list_commits - # - mcp__github__list_dependabot_alerts - # - mcp__github__list_discussion_categories - # - mcp__github__list_discussions - # - mcp__github__list_issue_types - # - mcp__github__list_issues - # - mcp__github__list_label - # - mcp__github__list_notifications # - mcp__github__list_pull_requests - # - mcp__github__list_releases - # - mcp__github__list_secret_scanning_alerts - # - mcp__github__list_starred_repositories - # - mcp__github__list_sub_issues - # - mcp__github__list_tags - # - mcp__github__list_workflow_jobs - # - mcp__github__list_workflow_run_artifacts - # - mcp__github__list_workflow_runs - # - mcp__github__list_workflows - # - mcp__github__pull_request_read - # - mcp__github__search_code - # - mcp__github__search_issues - # - mcp__github__search_orgs # - mcp__github__search_pull_requests - # - mcp__github__search_repositories - # - mcp__github__search_users timeout-minutes: 15 run: | set -o pipefail # Execute Claude Code CLI with prompt from file - claude --print --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools "Bash(/tmp/gh-aw/jqschema.sh),Bash(cat *),Bash(cat),Bash(claude-code --help),Bash(codex --help),Bash(copilot --help),Bash(date),Bash(echo),Bash(git *),Bash(grep *),Bash(grep),Bash(head),Bash(jq *),Bash(ls *),Bash(ls),Bash(make *),Bash(npm install *),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,WebFetch,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_sub_issues,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users" --debug --verbose --permission-mode bypassPermissions --output-format stream-json --settings /tmp/gh-aw/.claude/settings.json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" 2>&1 | tee /tmp/gh-aw/agent-stdio.log + claude --print --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools "Bash(/tmp/gh-aw/jqschema.sh),Bash(cat *),Bash(cat),Bash(claude-code --help),Bash(codex --help),Bash(copilot --help),Bash(date),Bash(echo),Bash(git *),Bash(grep *),Bash(grep),Bash(head),Bash(jq *),Bash(ls *),Bash(ls),Bash(make *),Bash(npm install *),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,WebFetch,Write,mcp__github__list_pull_requests,mcp__github__search_pull_requests" --debug --verbose --permission-mode bypassPermissions --output-format stream-json --settings /tmp/gh-aw/.claude/settings.json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" 2>&1 | tee /tmp/gh-aw/agent-stdio.log env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} DISABLE_TELEMETRY: "1" diff --git a/.github/workflows/cli-version-checker.md b/.github/workflows/cli-version-checker.md index 1370e347fbd..9a4b2afccda 100644 --- a/.github/workflows/cli-version-checker.md +++ b/.github/workflows/cli-version-checker.md @@ -11,6 +11,7 @@ network: allowed: [defaults, "registry.npmjs.org", "api.github.com", "ghcr.io"] imports: - shared/jqschema.md + - shared/check-existing-pr.md tools: web-fetch: bash: @@ -41,6 +42,16 @@ Monitor and update agentic CLI tools: Claude Code, GitHub Copilot CLI, OpenAI Co ## Process +### 0. Check for Existing Pull Request +Before starting version checks and updates, verify if there's already an open PR from this workflow: +- Use `search_pull_requests` with query: `repo:${{ github.repository }} is:pr is:open "[ca]" label:automation label:dependencies` +- If an existing open PR is found: + - Print message: "An open pull request already exists for CLI version updates" + - Include PR number, URL, and title + - Stop execution immediately +- If no existing PR found, proceed with version checks + +### 1. Check for Updates For each CLI/MCP server: 1. Fetch latest version from NPM registry or GitHub releases 2. Compare with current version in `./pkg/constants/constants.go` diff --git a/.github/workflows/shared/check-existing-pr.md b/.github/workflows/shared/check-existing-pr.md new file mode 100644 index 00000000000..daf1549ae2a --- /dev/null +++ b/.github/workflows/shared/check-existing-pr.md @@ -0,0 +1,105 @@ +--- +tools: + github: + allowed: + - search_pull_requests + - list_pull_requests +--- + +## Check for Existing Pull Request + +Before creating a new pull request, verify if there's already an open PR from this workflow to avoid duplicates. + +### Search Strategy + +Use both title prefix and labels for highly effective PR matching: + +1. **Search by title prefix and labels**: Use `search_pull_requests` with a query combining: + - Repository scope: `repo:${{ github.repository }}` + - PR filter: `is:pr` + - State filter: `is:open` + - Title pattern matching the workflow's title prefix + - Label filter matching the workflow's automation labels + +2. **Fallback search**: If needed, use `list_pull_requests` with `state: open` and manually filter by: + - Title prefix match + - Label match (both "automation" and workflow-specific labels) + +### Example Search Pattern + +For a workflow that creates PRs with: +- Title prefix: `[ca]` (CLI automation) +- Labels: `automation`, `dependencies` + +Use this search query: +``` +repo:${{ github.repository }} is:pr is:open "[ca]" label:automation label:dependencies +``` + +This pattern is highly effective because it combines: +- **Exact repository scoping**: Limits search to current repository +- **PR type filtering**: Only pull requests, not issues +- **State filtering**: Only open PRs +- **Title prefix matching**: Matches the unique workflow prefix in quotes for exact matching +- **Label combination**: Requires BOTH automation and workflow-specific labels + +### Implementation + +When checking for existing PRs: + +1. **First attempt** - Use `search_pull_requests`: + ``` + query: 'repo:${{ github.repository }} is:pr is:open "[your-prefix]" label:automation label:workflow-label' + ``` + +2. **Verify results**: Check if any PRs are returned + - If found: Extract PR number, URL, and branch name + - If none: Proceed with creating a new PR + +3. **Handle edge cases**: + - Multiple matching PRs: Use the most recently created/updated + - PR closed recently: Confirm it's truly open before reporting + +### Stop Condition + +If an existing open PR is found: +- Print a clear message: "An open pull request already exists for this workflow" +- Include PR details: number, URL, title +- Stop execution immediately without creating a new PR +- Exit successfully (not as an error) + +### Integration with Workflows + +Import this shared workflow in your agentic workflow frontmatter: +```yaml +imports: + - shared/check-existing-pr.md +``` + +Then, in your workflow instructions, add as the first step: +``` +### 0. Check for Existing Pull Request +Before starting any work, check if there's already an open pull request from this workflow: +- Use the search pattern with title prefix "[your-prefix]" and labels +- If found, print message and stop execution +- If not found, proceed with workflow tasks +``` + +### Search Query Examples + +**For CLI version checker** (`[ca]` prefix, `automation` + `dependencies` labels): +``` +repo:${{ github.repository }} is:pr is:open "[ca]" label:automation label:dependencies +``` + +**For tidy workflow** (`[tidy]` prefix, `automation` + `maintenance` labels): +``` +repo:${{ github.repository }} is:pr is:open "[tidy]" label:automation label:maintenance +``` + +**For security fixes** (`[security-fix]` prefix, `security` + `automated-fix` labels): +``` +repo:${{ github.repository }} is:pr is:open "[security-fix]" label:security label:automated-fix +``` + +This approach ensures extremely effective PR detection by requiring all criteria to match, making false positives virtually impossible. From dcedf1516e08a20d00ecf80bfe37e9a0abf40286 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 22:34:59 +0000 Subject: [PATCH 3/4] Add tests for check-existing-pr shared workflow import functionality Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/check_existing_pr_import_test.go | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 pkg/workflow/check_existing_pr_import_test.go diff --git a/pkg/workflow/check_existing_pr_import_test.go b/pkg/workflow/check_existing_pr_import_test.go new file mode 100644 index 00000000000..107552701b2 --- /dev/null +++ b/pkg/workflow/check_existing_pr_import_test.go @@ -0,0 +1,186 @@ +package workflow_test + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/githubnext/gh-aw/pkg/workflow" +) + +func TestCheckExistingPRImport(t *testing.T) { + // Create a temporary directory for test files + tempDir := t.TempDir() + + // Create shared directory + sharedDir := filepath.Join(tempDir, "shared") + if err := os.MkdirAll(sharedDir, 0755); err != nil { + t.Fatalf("Failed to create shared directory: %v", err) + } + + // Create the check-existing-pr.md shared file + sharedFilePath := filepath.Join(sharedDir, "check-existing-pr.md") + sharedFileContent := `--- +tools: + github: + allowed: + - search_pull_requests + - list_pull_requests +--- + +## Check for Existing Pull Request + +Instructions for checking existing PRs. +` + if err := os.WriteFile(sharedFilePath, []byte(sharedFileContent), 0644); err != nil { + t.Fatalf("Failed to write shared file: %v", err) + } + + // Create a workflow file that imports check-existing-pr + workflowPath := filepath.Join(tempDir, "test-workflow.md") + workflowContent := `--- +on: workflow_dispatch +permissions: + contents: read + actions: read +engine: claude +imports: + - shared/check-existing-pr.md +tools: + edit: +safe-outputs: + create-issue: + title-prefix: "[test] " + labels: [automation, test] +--- + +# Test Workflow + +Test workflow that uses check-existing-pr shared workflow. +` + if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil { + t.Fatalf("Failed to write workflow file: %v", err) + } + + // Compile the workflow + compiler := workflow.NewCompiler(false, "", "test") + if err := compiler.CompileWorkflow(workflowPath); err != nil { + t.Fatalf("CompileWorkflow failed: %v", err) + } + + // Read the generated lock file + lockFilePath := strings.TrimSuffix(workflowPath, ".md") + ".lock.yml" + lockFileContent, err := os.ReadFile(lockFilePath) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + + workflowData := string(lockFileContent) + + // Verify that the GitHub tools are included in the allowed tools + if !strings.Contains(workflowData, "mcp__github__search_pull_requests") { + t.Error("Expected compiled workflow to include search_pull_requests in allowed tools") + } + + if !strings.Contains(workflowData, "mcp__github__list_pull_requests") { + t.Error("Expected compiled workflow to include list_pull_requests in allowed tools") + } + + // Verify the instructions from the shared file are included + if !strings.Contains(workflowData, "Check for Existing Pull Request") { + t.Error("Expected compiled workflow to include instructions from shared file") + } + + // Verify GitHub MCP server configuration is present + if !strings.Contains(workflowData, "github") { + t.Error("Expected compiled workflow to contain GitHub MCP server configuration") + } +} + +func TestCheckExistingPRImportWithCopilot(t *testing.T) { + // Create a temporary directory for test files + tempDir := t.TempDir() + + // Create shared directory + sharedDir := filepath.Join(tempDir, "shared") + if err := os.MkdirAll(sharedDir, 0755); err != nil { + t.Fatalf("Failed to create shared directory: %v", err) + } + + // Create the check-existing-pr.md shared file + sharedFilePath := filepath.Join(sharedDir, "check-existing-pr.md") + sharedFileContent := `--- +tools: + github: + allowed: + - search_pull_requests + - list_pull_requests +--- + +## Check for Existing Pull Request + +Instructions for checking existing PRs. +` + if err := os.WriteFile(sharedFilePath, []byte(sharedFileContent), 0644); err != nil { + t.Fatalf("Failed to write shared file: %v", err) + } + + // Create a workflow file that imports check-existing-pr + workflowPath := filepath.Join(tempDir, "test-workflow.md") + workflowContent := `--- +on: workflow_dispatch +permissions: + contents: read +engine: copilot +imports: + - shared/check-existing-pr.md +tools: + edit: +safe-outputs: + create-pull-request: + title-prefix: "[test] " + labels: [automation, test] +--- + +# Test Workflow + +Test workflow that uses check-existing-pr shared workflow with Copilot. +` + if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil { + t.Fatalf("Failed to write workflow file: %v", err) + } + + // Compile the workflow + compiler := workflow.NewCompiler(false, "", "test") + if err := compiler.CompileWorkflow(workflowPath); err != nil { + t.Fatalf("CompileWorkflow failed: %v", err) + } + + // Read the generated lock file + lockFilePath := strings.TrimSuffix(workflowPath, ".md") + ".lock.yml" + lockFileContent, err := os.ReadFile(lockFilePath) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + + workflowData := string(lockFileContent) + + // For Copilot, verify that tools array is present in MCP config + if !strings.Contains(workflowData, `"tools":`) { + t.Error("Expected Copilot workflow to include tools array in MCP config") + } + + if !strings.Contains(workflowData, `"search_pull_requests"`) { + t.Error("Expected Copilot workflow to include search_pull_requests in tools array") + } + + if !strings.Contains(workflowData, `"list_pull_requests"`) { + t.Error("Expected Copilot workflow to include list_pull_requests in tools array") + } + + // Verify the instructions from the shared file are included + if !strings.Contains(workflowData, "Check for Existing Pull Request") { + t.Error("Expected compiled workflow to include instructions from shared file") + } +} From 6a2590fa16495b38b8befc2160234295acff341d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 22:52:47 +0000 Subject: [PATCH 4/4] Revert check-existing-pr shared workflow and test file Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../workflows/cli-version-checker.lock.yml | 163 +++++---------- .github/workflows/cli-version-checker.md | 11 -- .github/workflows/shared/check-existing-pr.md | 105 ---------- pkg/workflow/check_existing_pr_import_test.go | 186 ------------------ 4 files changed, 53 insertions(+), 412 deletions(-) delete mode 100644 .github/workflows/shared/check-existing-pr.md delete mode 100644 pkg/workflow/check_existing_pr_import_test.go diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 21c834bc8be..47b188a8743 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -6,7 +6,6 @@ # Resolved workflow manifest: # Imports: # - shared/jqschema.md -# - shared/check-existing-pr.md # # Job Dependency Graph: # ```mermaid @@ -1156,104 +1155,6 @@ jobs: # Now you know which fields exist and can use them in your analysis ``` - ## Check for Existing Pull Request - - Before creating a new pull request, verify if there's already an open PR from this workflow to avoid duplicates. - - ### Search Strategy - - Use both title prefix and labels for highly effective PR matching: - - 1. **Search by title prefix and labels**: Use `search_pull_requests` with a query combining: - - Repository scope: `repo:${{ github.repository }}` - - PR filter: `is:pr` - - State filter: `is:open` - - Title pattern matching the workflow's title prefix - - Label filter matching the workflow's automation labels - - 2. **Fallback search**: If needed, use `list_pull_requests` with `state: open` and manually filter by: - - Title prefix match - - Label match (both "automation" and workflow-specific labels) - - ### Example Search Pattern - - For a workflow that creates PRs with: - - Title prefix: `[ca]` (CLI automation) - - Labels: `automation`, `dependencies` - - Use this search query: - ``` - repo:${{ github.repository }} is:pr is:open "[ca]" label:automation label:dependencies - ``` - - This pattern is highly effective because it combines: - - **Exact repository scoping**: Limits search to current repository - - **PR type filtering**: Only pull requests, not issues - - **State filtering**: Only open PRs - - **Title prefix matching**: Matches the unique workflow prefix in quotes for exact matching - - **Label combination**: Requires BOTH automation and workflow-specific labels - - ### Implementation - - When checking for existing PRs: - - 1. **First attempt** - Use `search_pull_requests`: - ``` - query: 'repo:${{ github.repository }} is:pr is:open "[your-prefix]" label:automation label:workflow-label' - ``` - - 2. **Verify results**: Check if any PRs are returned - - If found: Extract PR number, URL, and branch name - - If none: Proceed with creating a new PR - - 3. **Handle edge cases**: - - Multiple matching PRs: Use the most recently created/updated - - PR closed recently: Confirm it's truly open before reporting - - ### Stop Condition - - If an existing open PR is found: - - Print a clear message: "An open pull request already exists for this workflow" - - Include PR details: number, URL, title - - Stop execution immediately without creating a new PR - - Exit successfully (not as an error) - - ### Integration with Workflows - - Import this shared workflow in your agentic workflow frontmatter: - ```yaml - imports: - - shared/check-existing-pr.md - ``` - - Then, in your workflow instructions, add as the first step: - ``` - ### 0. Check for Existing Pull Request - Before starting any work, check if there's already an open pull request from this workflow: - - Use the search pattern with title prefix "[your-prefix]" and labels - - If found, print message and stop execution - - If not found, proceed with workflow tasks - ``` - - ### Search Query Examples - - **For CLI version checker** (`[ca]` prefix, `automation` + `dependencies` labels): - ``` - repo:${{ github.repository }} is:pr is:open "[ca]" label:automation label:dependencies - ``` - - **For tidy workflow** (`[tidy]` prefix, `automation` + `maintenance` labels): - ``` - repo:${{ github.repository }} is:pr is:open "[tidy]" label:automation label:maintenance - ``` - - **For security fixes** (`[security-fix]` prefix, `security` + `automated-fix` labels): - ``` - repo:${{ github.repository }} is:pr is:open "[security-fix]" label:security label:automated-fix - ``` - - This approach ensures extremely effective PR detection by requiring all criteria to match, making false positives virtually impossible. - # CLI Version Checker Monitor and update agentic CLI tools: Claude Code, GitHub Copilot CLI, OpenAI Codex, and GitHub MCP Server. @@ -1262,16 +1163,6 @@ jobs: ## Process - ### 0. Check for Existing Pull Request - Before starting version checks and updates, verify if there's already an open PR from this workflow: - - Use `search_pull_requests` with query: `repo:${{ github.repository }} is:pr is:open "[ca]" label:automation label:dependencies` - - If an existing open PR is found: - - Print message: "An open pull request already exists for CLI version updates" - - Include PR number, URL, and title - - Stop execution immediately - - If no existing PR found, proceed with version checks - - ### 1. Check for Updates For each CLI/MCP server: 1. Fetch latest version from NPM registry or GitHub releases 2. Compare with current version in `./pkg/constants/constants.go` @@ -1613,13 +1504,65 @@ jobs: # - TodoWrite # - WebFetch # - Write + # - mcp__github__download_workflow_run_artifact + # - mcp__github__get_code_scanning_alert + # - mcp__github__get_commit + # - mcp__github__get_dependabot_alert + # - mcp__github__get_discussion + # - mcp__github__get_discussion_comments + # - mcp__github__get_file_contents + # - mcp__github__get_issue + # - mcp__github__get_issue_comments + # - mcp__github__get_job_logs + # - mcp__github__get_label + # - mcp__github__get_latest_release + # - mcp__github__get_me + # - mcp__github__get_notification_details + # - mcp__github__get_pull_request + # - mcp__github__get_pull_request_comments + # - mcp__github__get_pull_request_diff + # - mcp__github__get_pull_request_files + # - mcp__github__get_pull_request_review_comments + # - mcp__github__get_pull_request_reviews + # - mcp__github__get_pull_request_status + # - mcp__github__get_release_by_tag + # - mcp__github__get_secret_scanning_alert + # - mcp__github__get_tag + # - mcp__github__get_workflow_run + # - mcp__github__get_workflow_run_logs + # - mcp__github__get_workflow_run_usage + # - mcp__github__list_branches + # - mcp__github__list_code_scanning_alerts + # - mcp__github__list_commits + # - mcp__github__list_dependabot_alerts + # - mcp__github__list_discussion_categories + # - mcp__github__list_discussions + # - mcp__github__list_issue_types + # - mcp__github__list_issues + # - mcp__github__list_label + # - mcp__github__list_notifications # - mcp__github__list_pull_requests + # - mcp__github__list_releases + # - mcp__github__list_secret_scanning_alerts + # - mcp__github__list_starred_repositories + # - mcp__github__list_sub_issues + # - mcp__github__list_tags + # - mcp__github__list_workflow_jobs + # - mcp__github__list_workflow_run_artifacts + # - mcp__github__list_workflow_runs + # - mcp__github__list_workflows + # - mcp__github__pull_request_read + # - mcp__github__search_code + # - mcp__github__search_issues + # - mcp__github__search_orgs # - mcp__github__search_pull_requests + # - mcp__github__search_repositories + # - mcp__github__search_users timeout-minutes: 15 run: | set -o pipefail # Execute Claude Code CLI with prompt from file - claude --print --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools "Bash(/tmp/gh-aw/jqschema.sh),Bash(cat *),Bash(cat),Bash(claude-code --help),Bash(codex --help),Bash(copilot --help),Bash(date),Bash(echo),Bash(git *),Bash(grep *),Bash(grep),Bash(head),Bash(jq *),Bash(ls *),Bash(ls),Bash(make *),Bash(npm install *),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,WebFetch,Write,mcp__github__list_pull_requests,mcp__github__search_pull_requests" --debug --verbose --permission-mode bypassPermissions --output-format stream-json --settings /tmp/gh-aw/.claude/settings.json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" 2>&1 | tee /tmp/gh-aw/agent-stdio.log + claude --print --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools "Bash(/tmp/gh-aw/jqschema.sh),Bash(cat *),Bash(cat),Bash(claude-code --help),Bash(codex --help),Bash(copilot --help),Bash(date),Bash(echo),Bash(git *),Bash(grep *),Bash(grep),Bash(head),Bash(jq *),Bash(ls *),Bash(ls),Bash(make *),Bash(npm install *),Bash(pwd),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,WebFetch,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_sub_issues,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users" --debug --verbose --permission-mode bypassPermissions --output-format stream-json --settings /tmp/gh-aw/.claude/settings.json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" 2>&1 | tee /tmp/gh-aw/agent-stdio.log env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} DISABLE_TELEMETRY: "1" diff --git a/.github/workflows/cli-version-checker.md b/.github/workflows/cli-version-checker.md index 9a4b2afccda..1370e347fbd 100644 --- a/.github/workflows/cli-version-checker.md +++ b/.github/workflows/cli-version-checker.md @@ -11,7 +11,6 @@ network: allowed: [defaults, "registry.npmjs.org", "api.github.com", "ghcr.io"] imports: - shared/jqschema.md - - shared/check-existing-pr.md tools: web-fetch: bash: @@ -42,16 +41,6 @@ Monitor and update agentic CLI tools: Claude Code, GitHub Copilot CLI, OpenAI Co ## Process -### 0. Check for Existing Pull Request -Before starting version checks and updates, verify if there's already an open PR from this workflow: -- Use `search_pull_requests` with query: `repo:${{ github.repository }} is:pr is:open "[ca]" label:automation label:dependencies` -- If an existing open PR is found: - - Print message: "An open pull request already exists for CLI version updates" - - Include PR number, URL, and title - - Stop execution immediately -- If no existing PR found, proceed with version checks - -### 1. Check for Updates For each CLI/MCP server: 1. Fetch latest version from NPM registry or GitHub releases 2. Compare with current version in `./pkg/constants/constants.go` diff --git a/.github/workflows/shared/check-existing-pr.md b/.github/workflows/shared/check-existing-pr.md deleted file mode 100644 index daf1549ae2a..00000000000 --- a/.github/workflows/shared/check-existing-pr.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -tools: - github: - allowed: - - search_pull_requests - - list_pull_requests ---- - -## Check for Existing Pull Request - -Before creating a new pull request, verify if there's already an open PR from this workflow to avoid duplicates. - -### Search Strategy - -Use both title prefix and labels for highly effective PR matching: - -1. **Search by title prefix and labels**: Use `search_pull_requests` with a query combining: - - Repository scope: `repo:${{ github.repository }}` - - PR filter: `is:pr` - - State filter: `is:open` - - Title pattern matching the workflow's title prefix - - Label filter matching the workflow's automation labels - -2. **Fallback search**: If needed, use `list_pull_requests` with `state: open` and manually filter by: - - Title prefix match - - Label match (both "automation" and workflow-specific labels) - -### Example Search Pattern - -For a workflow that creates PRs with: -- Title prefix: `[ca]` (CLI automation) -- Labels: `automation`, `dependencies` - -Use this search query: -``` -repo:${{ github.repository }} is:pr is:open "[ca]" label:automation label:dependencies -``` - -This pattern is highly effective because it combines: -- **Exact repository scoping**: Limits search to current repository -- **PR type filtering**: Only pull requests, not issues -- **State filtering**: Only open PRs -- **Title prefix matching**: Matches the unique workflow prefix in quotes for exact matching -- **Label combination**: Requires BOTH automation and workflow-specific labels - -### Implementation - -When checking for existing PRs: - -1. **First attempt** - Use `search_pull_requests`: - ``` - query: 'repo:${{ github.repository }} is:pr is:open "[your-prefix]" label:automation label:workflow-label' - ``` - -2. **Verify results**: Check if any PRs are returned - - If found: Extract PR number, URL, and branch name - - If none: Proceed with creating a new PR - -3. **Handle edge cases**: - - Multiple matching PRs: Use the most recently created/updated - - PR closed recently: Confirm it's truly open before reporting - -### Stop Condition - -If an existing open PR is found: -- Print a clear message: "An open pull request already exists for this workflow" -- Include PR details: number, URL, title -- Stop execution immediately without creating a new PR -- Exit successfully (not as an error) - -### Integration with Workflows - -Import this shared workflow in your agentic workflow frontmatter: -```yaml -imports: - - shared/check-existing-pr.md -``` - -Then, in your workflow instructions, add as the first step: -``` -### 0. Check for Existing Pull Request -Before starting any work, check if there's already an open pull request from this workflow: -- Use the search pattern with title prefix "[your-prefix]" and labels -- If found, print message and stop execution -- If not found, proceed with workflow tasks -``` - -### Search Query Examples - -**For CLI version checker** (`[ca]` prefix, `automation` + `dependencies` labels): -``` -repo:${{ github.repository }} is:pr is:open "[ca]" label:automation label:dependencies -``` - -**For tidy workflow** (`[tidy]` prefix, `automation` + `maintenance` labels): -``` -repo:${{ github.repository }} is:pr is:open "[tidy]" label:automation label:maintenance -``` - -**For security fixes** (`[security-fix]` prefix, `security` + `automated-fix` labels): -``` -repo:${{ github.repository }} is:pr is:open "[security-fix]" label:security label:automated-fix -``` - -This approach ensures extremely effective PR detection by requiring all criteria to match, making false positives virtually impossible. diff --git a/pkg/workflow/check_existing_pr_import_test.go b/pkg/workflow/check_existing_pr_import_test.go deleted file mode 100644 index 107552701b2..00000000000 --- a/pkg/workflow/check_existing_pr_import_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package workflow_test - -import ( - "os" - "path/filepath" - "strings" - "testing" - - "github.com/githubnext/gh-aw/pkg/workflow" -) - -func TestCheckExistingPRImport(t *testing.T) { - // Create a temporary directory for test files - tempDir := t.TempDir() - - // Create shared directory - sharedDir := filepath.Join(tempDir, "shared") - if err := os.MkdirAll(sharedDir, 0755); err != nil { - t.Fatalf("Failed to create shared directory: %v", err) - } - - // Create the check-existing-pr.md shared file - sharedFilePath := filepath.Join(sharedDir, "check-existing-pr.md") - sharedFileContent := `--- -tools: - github: - allowed: - - search_pull_requests - - list_pull_requests ---- - -## Check for Existing Pull Request - -Instructions for checking existing PRs. -` - if err := os.WriteFile(sharedFilePath, []byte(sharedFileContent), 0644); err != nil { - t.Fatalf("Failed to write shared file: %v", err) - } - - // Create a workflow file that imports check-existing-pr - workflowPath := filepath.Join(tempDir, "test-workflow.md") - workflowContent := `--- -on: workflow_dispatch -permissions: - contents: read - actions: read -engine: claude -imports: - - shared/check-existing-pr.md -tools: - edit: -safe-outputs: - create-issue: - title-prefix: "[test] " - labels: [automation, test] ---- - -# Test Workflow - -Test workflow that uses check-existing-pr shared workflow. -` - if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil { - t.Fatalf("Failed to write workflow file: %v", err) - } - - // Compile the workflow - compiler := workflow.NewCompiler(false, "", "test") - if err := compiler.CompileWorkflow(workflowPath); err != nil { - t.Fatalf("CompileWorkflow failed: %v", err) - } - - // Read the generated lock file - lockFilePath := strings.TrimSuffix(workflowPath, ".md") + ".lock.yml" - lockFileContent, err := os.ReadFile(lockFilePath) - if err != nil { - t.Fatalf("Failed to read lock file: %v", err) - } - - workflowData := string(lockFileContent) - - // Verify that the GitHub tools are included in the allowed tools - if !strings.Contains(workflowData, "mcp__github__search_pull_requests") { - t.Error("Expected compiled workflow to include search_pull_requests in allowed tools") - } - - if !strings.Contains(workflowData, "mcp__github__list_pull_requests") { - t.Error("Expected compiled workflow to include list_pull_requests in allowed tools") - } - - // Verify the instructions from the shared file are included - if !strings.Contains(workflowData, "Check for Existing Pull Request") { - t.Error("Expected compiled workflow to include instructions from shared file") - } - - // Verify GitHub MCP server configuration is present - if !strings.Contains(workflowData, "github") { - t.Error("Expected compiled workflow to contain GitHub MCP server configuration") - } -} - -func TestCheckExistingPRImportWithCopilot(t *testing.T) { - // Create a temporary directory for test files - tempDir := t.TempDir() - - // Create shared directory - sharedDir := filepath.Join(tempDir, "shared") - if err := os.MkdirAll(sharedDir, 0755); err != nil { - t.Fatalf("Failed to create shared directory: %v", err) - } - - // Create the check-existing-pr.md shared file - sharedFilePath := filepath.Join(sharedDir, "check-existing-pr.md") - sharedFileContent := `--- -tools: - github: - allowed: - - search_pull_requests - - list_pull_requests ---- - -## Check for Existing Pull Request - -Instructions for checking existing PRs. -` - if err := os.WriteFile(sharedFilePath, []byte(sharedFileContent), 0644); err != nil { - t.Fatalf("Failed to write shared file: %v", err) - } - - // Create a workflow file that imports check-existing-pr - workflowPath := filepath.Join(tempDir, "test-workflow.md") - workflowContent := `--- -on: workflow_dispatch -permissions: - contents: read -engine: copilot -imports: - - shared/check-existing-pr.md -tools: - edit: -safe-outputs: - create-pull-request: - title-prefix: "[test] " - labels: [automation, test] ---- - -# Test Workflow - -Test workflow that uses check-existing-pr shared workflow with Copilot. -` - if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil { - t.Fatalf("Failed to write workflow file: %v", err) - } - - // Compile the workflow - compiler := workflow.NewCompiler(false, "", "test") - if err := compiler.CompileWorkflow(workflowPath); err != nil { - t.Fatalf("CompileWorkflow failed: %v", err) - } - - // Read the generated lock file - lockFilePath := strings.TrimSuffix(workflowPath, ".md") + ".lock.yml" - lockFileContent, err := os.ReadFile(lockFilePath) - if err != nil { - t.Fatalf("Failed to read lock file: %v", err) - } - - workflowData := string(lockFileContent) - - // For Copilot, verify that tools array is present in MCP config - if !strings.Contains(workflowData, `"tools":`) { - t.Error("Expected Copilot workflow to include tools array in MCP config") - } - - if !strings.Contains(workflowData, `"search_pull_requests"`) { - t.Error("Expected Copilot workflow to include search_pull_requests in tools array") - } - - if !strings.Contains(workflowData, `"list_pull_requests"`) { - t.Error("Expected Copilot workflow to include list_pull_requests in tools array") - } - - // Verify the instructions from the shared file are included - if !strings.Contains(workflowData, "Check for Existing Pull Request") { - t.Error("Expected compiled workflow to include instructions from shared file") - } -}