From 40aee1e8a8a15a9d013ab8b4045831b64f899504 Mon Sep 17 00:00:00 2001 From: AR <17565234+whoisarpit@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:44:51 +0800 Subject: [PATCH 1/4] Add option to specify branch prefix for all patchflows used in ci --- patchwork/patchflows/AutoFix/AutoFix.py | 7 +++++-- .../GenerateCodeUsageExample.py | 9 ++++++--- .../patchflows/GenerateDiagram/GenerateDiagram.py | 9 ++++++--- .../GenerateDocstring/GenerateDocstring.py | 14 +++++++++----- .../patchflows/GenerateREADME/GenerateREADME.py | 3 ++- .../GenerateUnitTests/GenerateUnitTests.py | 9 ++++++--- patchwork/patchflows/ResolveIssue/ResolveIssue.py | 13 ++++++++----- 7 files changed, 42 insertions(+), 22 deletions(-) diff --git a/patchwork/patchflows/AutoFix/AutoFix.py b/patchwork/patchflows/AutoFix/AutoFix.py index b548629c8..8c98d6ba9 100644 --- a/patchwork/patchflows/AutoFix/AutoFix.py +++ b/patchwork/patchflows/AutoFix/AutoFix.py @@ -72,8 +72,11 @@ def __init__(self, inputs: dict): "compatibility": ["C. Compatibility Risk:", "D. Fixed Code:"], "patch": ["D. Fixed Code:", "```", "\n", "```"], } - final_inputs["pr_title"] = f"PatchWork {self.__class__.__name__}" - final_inputs["branch_prefix"] = f"{self.__class__.__name__.lower()}-" + if "pr_title" not in final_inputs.keys(): + final_inputs["pr_title"] = f"PatchWork {self.__class__.__name__}" + + if "branch_prefix" not in final_inputs.keys(): + final_inputs["branch_prefix"] = f"{self.__class__.__name__.lower()}-" validate_steps_with_inputs( set(final_inputs.keys()).union({"prompt_values"}), ScanSemgrep, ExtractCode, LLM, ModifyCode, PR diff --git a/patchwork/patchflows/GenerateCodeUsageExample/GenerateCodeUsageExample.py b/patchwork/patchflows/GenerateCodeUsageExample/GenerateCodeUsageExample.py index 5afac3709..14de647bd 100644 --- a/patchwork/patchflows/GenerateCodeUsageExample/GenerateCodeUsageExample.py +++ b/patchwork/patchflows/GenerateCodeUsageExample/GenerateCodeUsageExample.py @@ -29,8 +29,11 @@ def __init__(self, inputs): if "prompt_template_file" not in final_inputs: final_inputs["prompt_template_file"] = _DEFAULT_PROMPT_JSON - final_inputs["pr_title"] = f"PatchWork Usage Example generated" - final_inputs["branch_prefix"] = f"{self.__class__.__name__.lower()}-" + if "pr_title" not in final_inputs.keys(): + final_inputs["pr_title"] = "PatchWork Usage Example generated" + + if "branch_prefix" not in final_inputs.keys(): + final_inputs["branch_prefix"] = f"{self.__class__.__name__.lower()}-" validate_steps_with_inputs( set(final_inputs.keys()).union({"prompt_values", "files_to_patch"}), LLM, CallCode2Prompt, ModifyCode, PR @@ -50,7 +53,7 @@ def run(self): outputs = ModifyCode(self.inputs).run() self.inputs.update(outputs) number = len(self.inputs["modified_code_files"]) - self.inputs["pr_header"] = f"This pull request adds usage example." + self.inputs["pr_header"] = "This pull request adds usage example." outputs = PR(self.inputs).run() self.inputs.update(outputs) diff --git a/patchwork/patchflows/GenerateDiagram/GenerateDiagram.py b/patchwork/patchflows/GenerateDiagram/GenerateDiagram.py index b59b95ee1..15eea19fd 100644 --- a/patchwork/patchflows/GenerateDiagram/GenerateDiagram.py +++ b/patchwork/patchflows/GenerateDiagram/GenerateDiagram.py @@ -25,8 +25,11 @@ def __init__(self, inputs): if "prompt_template_file" not in final_inputs: final_inputs["prompt_template_file"] = _DEFAULT_PROMPT_JSON - final_inputs["pr_title"] = f"PatchWork System Architecture Diagram" - final_inputs["branch_prefix"] = f"{self.__class__.__name__.lower()}-" + if "pr_title" not in final_inputs.keys(): + final_inputs["pr_title"] = "PatchWork System Architecture Diagram" + + if "branch_prefix" not in final_inputs.keys(): + final_inputs["branch_prefix"] = f"{self.__class__.__name__.lower()}-" validate_steps_with_inputs( set(final_inputs.keys()).union({"prompt_values", "files_to_patch"}), LLM, CallCode2Prompt, ModifyCode, PR @@ -44,7 +47,7 @@ def run(self): self.inputs.update(outputs) outputs = ModifyCode(self.inputs).run() self.inputs.update(outputs) - self.inputs["pr_header"] = f"This pull request from patchwork generates system architecture diagram." + self.inputs["pr_header"] = "This pull request from patchwork generates system architecture diagram." outputs = PR(self.inputs).run() self.inputs.update(outputs) diff --git a/patchwork/patchflows/GenerateDocstring/GenerateDocstring.py b/patchwork/patchflows/GenerateDocstring/GenerateDocstring.py index 108d02d24..aad33b08b 100644 --- a/patchwork/patchflows/GenerateDocstring/GenerateDocstring.py +++ b/patchwork/patchflows/GenerateDocstring/GenerateDocstring.py @@ -47,8 +47,12 @@ def __init__(self, inputs: dict): if "prompt_template_file" not in final_inputs.keys(): final_inputs["prompt_template_file"] = _DEFAULT_PROMPT_JSON - final_inputs["pr_title"] = f"PatchWork {self.__class__.__name__}" - final_inputs["branch_prefix"] = f"{self.__class__.__name__.lower()}-" + if "pr_title" not in final_inputs.keys(): + final_inputs["pr_title"] = f"PatchWork {self.__class__.__name__}" + + if "branch_prefix" not in final_inputs.keys(): + final_inputs["branch_prefix"] = f"{self.__class__.__name__.lower()}-" + final_inputs["context_grouping"] = "FUNCTION" final_inputs["allow_overlap_contexts"] = False final_inputs["force_code_contexts"] = final_inputs.get("rewrite_existing", False) @@ -75,9 +79,9 @@ def run(self) -> dict: self.inputs.update(outputs) # Commit changes and create PR - self.inputs[ - "pr_header" - ] = f'This pull request from patchwork fixes {len(self.inputs["prompt_values"])} docstrings.' + self.inputs["pr_header"] = ( + f"This pull request from patchwork fixes {len(self.inputs['prompt_values'])} docstrings." + ) outputs = PR(self.inputs).run() self.inputs.update(outputs) diff --git a/patchwork/patchflows/GenerateREADME/GenerateREADME.py b/patchwork/patchflows/GenerateREADME/GenerateREADME.py index 0be781791..74945ef96 100644 --- a/patchwork/patchflows/GenerateREADME/GenerateREADME.py +++ b/patchwork/patchflows/GenerateREADME/GenerateREADME.py @@ -51,7 +51,8 @@ def __init__(self, inputs: dict): else: final_inputs["folder_path"] = Path(final_inputs["folder_path"]) - final_inputs["pr_title"] = f"PatchWork {self.__class__.__name__}" + if "pr_title" not in final_inputs.keys(): + final_inputs["pr_title"] = f"PatchWork {self.__class__.__name__}" validate_steps_with_inputs( set(final_inputs.keys()).union({"prompt_values", "files_to_patch"}), CallCode2Prompt, LLM, ModifyCode, PR diff --git a/patchwork/patchflows/GenerateUnitTests/GenerateUnitTests.py b/patchwork/patchflows/GenerateUnitTests/GenerateUnitTests.py index b82abe294..009260830 100644 --- a/patchwork/patchflows/GenerateUnitTests/GenerateUnitTests.py +++ b/patchwork/patchflows/GenerateUnitTests/GenerateUnitTests.py @@ -29,8 +29,11 @@ def __init__(self, inputs): if "prompt_template_file" not in final_inputs: final_inputs["prompt_template_file"] = _DEFAULT_PROMPT_JSON - final_inputs["pr_title"] = f"PatchWork Unit Tests generated" - final_inputs["branch_prefix"] = f"{self.__class__.__name__.lower()}-" + if "pr_title" not in final_inputs.keys(): + final_inputs["pr_title"] = "PatchWork Unit Tests generated" + + if "branch_prefix" not in final_inputs.keys(): + final_inputs["branch_prefix"] = f"{self.__class__.__name__.lower()}-" validate_steps_with_inputs( set(final_inputs.keys()).union({"prompt_values", "files_to_patch"}), LLM, CallCode2Prompt, ModifyCode, PR @@ -50,7 +53,7 @@ def run(self): outputs = ModifyCode(self.inputs).run() self.inputs.update(outputs) number = len(self.inputs["modified_code_files"]) - self.inputs["pr_header"] = f"This pull request from patchwork adds tests." + self.inputs["pr_header"] = "This pull request from patchwork adds tests." outputs = PR(self.inputs).run() self.inputs.update(outputs) diff --git a/patchwork/patchflows/ResolveIssue/ResolveIssue.py b/patchwork/patchflows/ResolveIssue/ResolveIssue.py index 850318d30..67b507c5e 100644 --- a/patchwork/patchflows/ResolveIssue/ResolveIssue.py +++ b/patchwork/patchflows/ResolveIssue/ResolveIssue.py @@ -20,8 +20,11 @@ def __init__(self, inputs: dict): final_inputs = yaml.safe_load(_DEFAULT_INPUT_FILE.read_text()) or dict() final_inputs.update(inputs) - final_inputs["pr_title"] = f"PatchWork {self.__class__.__name__}" - final_inputs["branch_prefix"] = f"{self.__class__.__name__.lower()}-" + if "pr_title" not in final_inputs.keys(): + final_inputs["pr_title"] = f"PatchWork {self.__class__.__name__}" + + if "branch_prefix" not in final_inputs.keys(): + final_inputs["branch_prefix"] = f"{self.__class__.__name__.lower()}-" validate_steps_with_inputs( {"issue_description"}.union(final_inputs.keys()), @@ -46,8 +49,8 @@ def run(self) -> dict: issue=outputs["issue_description"], ), system_prompt="""\ -You are a senior software engineer tasked to analyze a issue. -Your analysis will be used to guide the junior engineer to resolve this issue. +You are a senior software engineer tasked to analyze a issue. +Your analysis will be used to guide the junior engineer to resolve this issue. """, user_prompt="""\ @@ -130,7 +133,7 @@ def run(self) -> dict: 1. Edit the sourcecode of the repo to resolve the issue 2. Think about edge cases and make sure your fix handles them as well -I've already taken care of all changes to any of the test files described in the PR. +I've already taken care of all changes to any of the test files described in the PR. This means you DON'T have to modify the testing logic or any of the tests in any way! """, max_llm_calls=200, From 91a2e6009fbcba991fb2ea0b0ede5d339e6a52d5 Mon Sep 17 00:00:00 2001 From: AR <17565234+whoisarpit@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:45:11 +0800 Subject: [PATCH 2/4] Update ci tests and add additional cron job to clear the PRs --- .github/workflows/close-stale-ci-prs.yml | 41 ++++++++++++++++++++++++ .github/workflows/test.yml | 10 +++++- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/close-stale-ci-prs.yml diff --git a/.github/workflows/close-stale-ci-prs.yml b/.github/workflows/close-stale-ci-prs.yml new file mode 100644 index 000000000..a7dbab564 --- /dev/null +++ b/.github/workflows/close-stale-ci-prs.yml @@ -0,0 +1,41 @@ +name: Close Stale CI PRs + +on: + schedule: + # Run every hour + - cron: "0 * * * *" + workflow_dispatch: + +jobs: + close-stale-prs: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Close stale PRs + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Get all open PRs with patchwork-ci- branches + prs=$(gh pr list --state open --json number,headRefName,createdAt --jq '.[] | select(.headRefName | startswith("patchwork-ci-")) | "\(.number) \(.createdAt)"') + + while IFS= read -r line; do + if [ -z "$line" ]; then + continue + fi + + pr_number=$(echo "$line" | awk '{print $1}') + created_at=$(echo "$line" | awk '{print $2}') + + # Convert to Unix timestamp + created_ts=$(date -d "$created_at" +%s) + current_ts=$(date +%s) + age_hours=$(( (current_ts - created_ts) / 3600 )) + + if [ $age_hours -gt 1 ]; then + echo "Closing PR #$pr_number (age: $age_hours hours)" + gh pr comment $pr_number --body "This PR has been automatically closed because it's been open for more than 1 hour. This is a CI-generated PR and should be reviewed and merged promptly if valid." + gh pr close $pr_number + fi + done <<< "$prs" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 595d5b9db..b21d99813 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,10 +4,12 @@ on: push: branches-ignore: - main + - patchwork-ci-* - autofix-* - dependencyupgrade-* - generatereadme-* - generatedocstring-* + - generatediagram-* - generateunittests-* - generatecodeusageexample-* - resolveissue-* @@ -104,6 +106,7 @@ jobs: patchwork AutoFix --log debug \ --patched_api_key=${{ secrets.PATCHED_API_KEY }} \ --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ + --branch_prefix=patchwork-ci-autofix- \ --force_pr_creation \ --disable_telemetry @@ -114,6 +117,7 @@ jobs: --libraries_api_key=${{ secrets.LIBRARIES_KEY }} \ --patched_api_key=${{ secrets.PATCHED_API_KEY }} \ --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ + --branch_prefix=patchwork-ci-dependencyupgrade- \ --language=python \ --force_pr_creation \ --disable_telemetry @@ -169,6 +173,7 @@ jobs: --patched_api_key=${{ secrets.PATCHED_API_KEY }} \ --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ --base_path=tests/cicd/generate_docstring \ + --branch_prefix=patchwork-ci-generatedocstring- \ --disable_telemetry - name: Generate Diagram @@ -178,6 +183,7 @@ jobs: --patched_api_key=${{ secrets.PATCHED_API_KEY }} \ --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ --folder_path=patchwork/steps \ + --branch_prefix=patchwork-ci-generatediagram- \ --disable_telemetry - name: Generate UnitTests @@ -187,6 +193,7 @@ jobs: --patched_api_key=${{ secrets.PATCHED_API_KEY }} \ --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ --folder_path=tests/cicd/generate_docstring \ + --branch_prefix=patchwork-ci-generateunittests- \ --disable_telemetry - name: Generate Code Usage Example @@ -196,6 +203,7 @@ jobs: --patched_api_key=${{ secrets.PATCHED_API_KEY }} \ --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ --folder_path=tests/cicd/generate_docstring \ + --branch_prefix=patchwork-ci-generatecodeusageexample- \ --disable_telemetry - name: Generate README @@ -218,7 +226,7 @@ jobs: --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ --filter=*.py \ --folder_path=$dir \ - --branch_prefix=generatereadme-$branch_name \ + --branch_prefix=patchwork-ci-generatereadme \ --disable_telemetry else echo "Found README.md in $dir" From fee13e1e35a8305906b4ee75a86171ec7d84edd7 Mon Sep 17 00:00:00 2001 From: AR <17565234+whoisarpit@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:46:29 +0800 Subject: [PATCH 3/4] Add auto deletion of 1 week+ old branches --- .github/workflows/close-stale-ci-prs.yml | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/close-stale-ci-prs.yml b/.github/workflows/close-stale-ci-prs.yml index a7dbab564..54f8dbf7a 100644 --- a/.github/workflows/close-stale-ci-prs.yml +++ b/.github/workflows/close-stale-ci-prs.yml @@ -39,3 +39,29 @@ jobs: gh pr close $pr_number fi done <<< "$prs" + + - name: Delete stale branches + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Get all branches with patchwork-ci- prefix + branches=$(gh api repos/${{ github.repository }}/branches --jq '.[] | select(.name | startswith("patchwork-ci-")) | "\(.name) \(.commit.commit.committer.date)"') + + while IFS= read -r line; do + if [ -z "$line" ]; then + continue + fi + + branch_name=$(echo "$line" | awk '{print $1}') + created_at=$(echo "$line" | awk '{print $2}') + + # Convert to Unix timestamp + created_ts=$(date -d "$created_at" +%s) + current_ts=$(date +%s) + age_days=$(( (current_ts - created_ts) / 86400 )) + + if [ $age_days -gt 7 ]; then + echo "Deleting branch $branch_name (age: $age_days days)" + gh api -X DELETE repos/${{ github.repository }}/git/refs/heads/$branch_name + fi + done <<< "$branches" From 636cd144fdf9d3eec2a770a776dda1b7a8c3d882 Mon Sep 17 00:00:00 2001 From: AR <17565234+whoisarpit@users.noreply.github.com> Date: Mon, 14 Apr 2025 15:02:43 +0800 Subject: [PATCH 4/4] Plain output in test.yml --- .github/workflows/test.yml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b21d99813..61b499156 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -108,7 +108,8 @@ jobs: --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ --branch_prefix=patchwork-ci-autofix- \ --force_pr_creation \ - --disable_telemetry + --disable_telemetry \ + --plain - name: Dependency Upgrade run: | @@ -120,7 +121,8 @@ jobs: --branch_prefix=patchwork-ci-dependencyupgrade- \ --language=python \ --force_pr_creation \ - --disable_telemetry + --disable_telemetry \ + --plain main-test: runs-on: ubuntu-latest @@ -164,7 +166,8 @@ jobs: --anthropic_api_key=${{ secrets.ANTHROPIC_API_KEY }} \ --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ --pr_url=https://github.com/patched-codes/patchwork/pull/${{ steps.findPr.outputs.number }} \ - --disable_telemetry + --disable_telemetry \ + --plain - name: Generate Docstring run: | @@ -174,7 +177,8 @@ jobs: --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ --base_path=tests/cicd/generate_docstring \ --branch_prefix=patchwork-ci-generatedocstring- \ - --disable_telemetry + --disable_telemetry \ + --plain - name: Generate Diagram run: | @@ -184,7 +188,8 @@ jobs: --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ --folder_path=patchwork/steps \ --branch_prefix=patchwork-ci-generatediagram- \ - --disable_telemetry + --disable_telemetry \ + --plain - name: Generate UnitTests run: | @@ -194,7 +199,8 @@ jobs: --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ --folder_path=tests/cicd/generate_docstring \ --branch_prefix=patchwork-ci-generateunittests- \ - --disable_telemetry + --disable_telemetry \ + --plain - name: Generate Code Usage Example run: | @@ -204,7 +210,8 @@ jobs: --github_api_key=${{ secrets.SCM_GITHUB_KEY }} \ --folder_path=tests/cicd/generate_docstring \ --branch_prefix=patchwork-ci-generatecodeusageexample- \ - --disable_telemetry + --disable_telemetry \ + --plain - name: Generate README run: | @@ -227,7 +234,8 @@ jobs: --filter=*.py \ --folder_path=$dir \ --branch_prefix=patchwork-ci-generatereadme \ - --disable_telemetry + --disable_telemetry \ + --plain else echo "Found README.md in $dir" fi