5
5
types : [submitted]
6
6
7
7
jobs :
8
- auto-fix-review :
8
+ check-membership :
9
9
if : github.event.review.state == 'changes_requested' && contains(github.event.pull_request.title, '[Aider PR]')
10
- runs-on : ubicloud-standard-8
10
+ runs-on : ubicloud-standard-2
11
+ outputs :
12
+ is_member : ${{ steps.check-membership.outputs.is_member }}
13
+ steps :
14
+ - name : Check organization membership
15
+ id : check-membership
16
+ env :
17
+ REVIEWER : ${{ github.event.review.user.login }}
18
+ ORG_ACCESS_TOKEN : ${{ secrets.ORG_ACCESS_TOKEN }}
19
+ run : |
20
+ ORG="windmill-labs"
21
+ STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
22
+ -H "Authorization: token $ORG_ACCESS_TOKEN" \
23
+ -H "Accept: application/vnd.github+json" \
24
+ -H "X-GitHub-Api-Version: 2022-11-28" \
25
+ "https://api.github.com/orgs/$ORG/members/$REVIEWER")
26
+
27
+ if [ "$STATUS" -eq 204 ]; then
28
+ echo "is_member=true" >> $GITHUB_OUTPUT
29
+ else
30
+ echo "is_member=false" >> $GITHUB_OUTPUT
31
+ fi
32
+
33
+ check-and-prepare :
34
+ needs : check-membership
35
+ if : github.event.review.state == 'changes_requested' && contains(github.event.pull_request.title, '[Aider PR]') && needs.check-membership.outputs.is_member == 'true'
36
+ runs-on : ubicloud-standard-2
11
37
permissions :
12
38
contents : write
13
39
pull-requests : write
40
+ outputs :
41
+ prompt_content : ${{ steps.prepare_prompt.outputs.prompt_content }}
14
42
env :
15
43
GEMINI_API_KEY : ${{ secrets.GOOGLE_API_KEY }}
16
44
GOOGLE_API_KEY : ${{ secrets.GOOGLE_API_KEY }}
@@ -19,198 +47,46 @@ jobs:
19
47
WINDMILL_TOKEN : ${{ secrets.WINDMILL_TOKEN }}
20
48
21
49
steps :
22
- - name : Harden Runner
23
- uses : step-security/harden-runner@v2
24
- with :
25
- egress-policy : audit
26
-
27
- - name : Check out code
28
- uses : actions/checkout@v4
29
- with :
30
- fetch-depth : 0
31
-
32
- - name : Configure Git User
33
- run : |
34
- git config --global user.name "github-actions[bot]"
35
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
36
-
37
- - name : Checkout PR Branch
50
+ - name : Acknowledge Request
38
51
env :
39
52
GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
53
+ GITHUB_REPOSITORY : ${{ github.repository }}
40
54
run : |
41
- echo "PR review trigger: Checking out PR branch..."
42
- PR_NUMBER=${{ github.event.pull_request.number }}
43
- PR_HEAD_REF=$(gh pr view $PR_NUMBER --json headRefName -q .headRefName --repo $GITHUB_REPOSITORY)
44
- if [[ -z "$PR_HEAD_REF" || "$PR_HEAD_REF" == "null" ]]; then
45
- echo "::error::Could not determine PR head branch for PR #$PR_NUMBER via gh CLI."
46
- exit 1
47
- fi
48
- echo "Checking out PR head branch: $PR_HEAD_REF for PR #$PR_NUMBER"
49
- git fetch origin "refs/heads/${PR_HEAD_REF}:refs/remotes/origin/${PR_HEAD_REF}" --no-tags
50
- git checkout "$PR_HEAD_REF"
51
- echo "Successfully checked out branch $(git rev-parse --abbrev-ref HEAD)"
52
-
53
- - name : Set up Python
54
- uses : actions/setup-python@v5
55
- with :
56
- python-version : ' 3.12'
57
-
58
- - name : Install Aider and Dependencies
59
- run : |
60
- python -m pip install aider-install; aider-install
61
- pip install -U google-generativeai
62
- sudo apt-get update && sudo apt-get install -y jq
55
+ echo "Commenting on PR #${{ github.event.pull_request.number }} to acknowledge the /aider command."
56
+ gh pr comment ${{ github.event.pull_request.number }} --body "🤖 Aider is starting to work on your request. Please be patient, this might take a few minutes." --repo $GITHUB_REPOSITORY
63
57
64
- - name : Generate Prompt from Review
65
- id : generate_prompt
58
+ - name : Prepare prompt for Aider
59
+ id : prepare_prompt
66
60
shell : bash
61
+ env :
62
+ GITHUB_REPOSITORY : ${{ github.repository }}
63
+ PR_NUMBER : ${{ github.event.pull_request.number }}
64
+ REVIEW_BODY : ${{ github.event.review.body }}
67
65
run : |
68
- mkdir -p .github/aider
69
- PROMPT_FILE_PATH=".github/aider/review-prompt.txt"
70
-
71
- # Get PR review body
72
- REVIEW_BODY="${{ github.event.review.body }}"
73
- REVIEW_BODY_Q=$(printf '%q' "$REVIEW_BODY")
74
-
75
- PR_NUMBER="${{ github.event.pull_request.number }}"
76
-
77
- # Get PR description for context NOT USED FOR NOW
78
- # PR_DETAILS=$(gh pr view $PR_NUMBER --json title,body --repo $GITHUB_REPOSITORY)
79
- # PR_TITLE=$(echo "$PR_DETAILS" | jq -r .title)
80
- # PR_BODY=$(echo "$PR_DETAILS" | jq -r .body)
66
+ REVIEW_BODY_ESCAPED="${REVIEW_BODY//\\/\\\\}"
67
+ REVIEW_BODY_ESCAPED="${REVIEW_BODY_ESCAPED//\"/\\\"}"
81
68
82
- # Get all PR review comments
83
69
ALL_REVIEW_COMMENTS=$(gh api \
84
70
-H "Accept: application/vnd.github+json" \
85
71
-H "X-GitHub-Api-Version: 2022-11-28" \
86
- /repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/comments \
87
- | jq '[.[] | {diff_hunk: .diff_hunk, path: .path, body: .body}]')
88
-
89
- BASE_PROMPT="Fix the following issues in the PR based on the review feedback. The review body is prepended with REVIEW. The review comments are prepended with REVIEW_COMMENTS. The review body and comments are separated by a blank line."
90
- COMPLETE_PROMPT=$(printf "%s\nREVIEW:\n%s\nREVIEW_COMMENTS:\n%s" \
91
- "$BASE_PROMPT" "$REVIEW_BODY_Q" "$ALL_REVIEW_COMMENTS")
92
- echo "$COMPLETE_PROMPT" > "$PROMPT_FILE_PATH"
93
- echo "PROMPT_FILE_PATH=$PROMPT_FILE_PATH" >> $GITHUB_OUTPUT
94
-
95
- - name : Probe Chat for Relevant Files
96
- id : probe_files
97
- env :
98
- PROMPT_CONTENT_FILE : ${{ steps.generate_prompt.outputs.PROMPT_FILE_PATH }}
99
- run : |
100
- echo "Running probe-chat to find relevant files..."
101
- if [[ ! -f "$PROMPT_CONTENT_FILE" ]]; then
102
- echo "::error::Prompt file $PROMPT_CONTENT_FILE not found!"
103
- exit 1
104
- fi
105
- PROMPT_CONTENT=$(cat "$PROMPT_CONTENT_FILE")
106
- if [ -z "$PROMPT_CONTENT" ]; then
107
- echo "::error::Prompt content is empty!"
108
- exit 1
109
- fi
72
+ /repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/comments)
110
73
111
- PROMPT_ESCAPED =$(jq -Rs . <<< "$PROMPT_CONTENT ")
74
+ FORMATTED_COMMENTS =$(jq '[.[] | {diff_hunk: .diff_hunk, path: .path, body: .body}]' <<< "$ALL_REVIEW_COMMENTS ")
112
75
113
- MESSAGE_FOR_PROBE=$(jq -n --arg prompt_escaped "$PROMPT_ESCAPED" \
114
- '{ "message": "I'\''m giving you a request that needs to be implemented. Your role is ONLY to give me the files that are relevant to the request and nothing else. The request is prepended with the word REQUEST.\\nREQUEST: \($prompt_escaped). Give me all the files relevant to this request. Your output MUST be a single json array that can be parsed with programatic json parsing, with the relevant files. Files can be rust or typescript or javascript files. DO NOT INCLUDE ANY OTHER TEXT IN YOUR OUTPUT. ONLY THE JSON ARRAY. Example of output: [\"file1.py\", \"file2.py\"]" }' | jq -r .message)
115
-
116
- set -o pipefail
117
- PROBE_OUTPUT=$(npx --yes @buger/probe-chat@latest --max-iterations 50 --model-name gemini-2.5-pro-preview-05-06 --message "$MESSAGE_FOR_PROBE") || {
118
- echo "::error::probe-chat command failed. Output:"
119
- echo "$PROBE_OUTPUT"
120
- exit 1
121
- }
122
- set +o pipefail
123
- echo "Probe-chat raw output:"
124
- echo "$PROBE_OUTPUT"
125
-
126
- JSON_FILES=$(echo "$PROBE_OUTPUT" | sed -n '/^\s*\[/,$p' | sed '/^\s*\]/q')
127
- echo "Extracted JSON block:"
128
- echo "$JSON_FILES"
129
-
130
- FILES_LIST=$(echo "$JSON_FILES" | jq -e -r '[.[] | select(type == "string" and . != "" and . != null and (endswith("/") | not))] | map(@sh) | join(" ")' || echo "")
131
-
132
- if [[ -z "$FILES_LIST" ]]; then
133
- echo "::warning::probe-chat did not identify any relevant files."
134
- exit 1
135
- fi
136
-
137
- echo "Formatted files list for aider: $FILES_LIST"
138
- echo "FILES_TO_EDIT=$FILES_LIST" >> $GITHUB_ENV
139
-
140
- - name : Run Aider with review prompt
141
- run : |
142
- aider \
143
- --read CLAUDE.md \
144
- ${{ env.FILES_TO_EDIT }} \
145
- --model gemini/gemini-2.5-pro-preview-05-06 \
146
- --message-file .github/aider/review-prompt.txt \
147
- --yes \
148
- --no-check-update \
149
- --auto-commits \
150
- --no-analytics \
151
- --no-gitignore \
152
- | tee .github/aider/aider-output.txt || true
153
- echo "Aider command completed. Output saved to .github/aider/aider-output.txt"
154
- # Check if there are any changes to commit
155
- if [[ -z "$(git status --porcelain)" ]]; then
156
- echo "No changes detected after running Aider."
157
- echo "HAS_CHANGES=false" >> $GITHUB_OUTPUT
158
- exit 0
159
- fi
160
-
161
- - name : Clean up prompt file
162
- if : always()
163
- run : rm -f .github/aider/review-prompt.txt
164
-
165
- - name : Commit and Push Changes
166
- id : commit_and_push
167
- if : ${{ success() }}
168
- run : |
169
- CURRENT_BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
170
- echo "Attempting to push changes to PR branch $CURRENT_BRANCH_NAME for PR #${{ github.event.pull_request.number }}"
171
-
172
- # Pull latest changes to avoid rejection due to non-fast-forward
173
- git config pull.rebase true
174
- git pull origin $CURRENT_BRANCH_NAME
175
-
176
- if git push origin $CURRENT_BRANCH_NAME; then
177
- echo "Push to $CURRENT_BRANCH_NAME successful."
178
- echo "CHANGES_APPLIED=true" >> $GITHUB_OUTPUT
179
- else
180
- echo "::warning::Push to PR branch $CURRENT_BRANCH_NAME failed."
181
- echo "CHANGES_APPLIED=false" >> $GITHUB_OUTPUT
182
- fi
183
-
184
- - name : Comment on PR
185
- if : success()
186
- env :
187
- GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
188
- PR_NUM : ${{ github.event.pull_request.number }}
189
- run : |
190
- # Create comment body in a temporary file to avoid command line length limits
191
- if [[ "${{ steps.commit_and_push.outputs.CHANGES_APPLIED }}" == "true" ]]; then
192
- cat > /tmp/pr-comment.md << EOL
193
- 🤖 I've automatically addressed the feedback based on the review.
194
-
195
- ## Aider Output
196
- \`\`\`
197
- $(cat .github/aider/aider-output.txt || echo 'No output available')
198
- \`\`\`
199
-
200
- Please review the changes and let me know if further adjustments are needed.
201
- EOL
202
- else
203
- cat > /tmp/pr-comment.md << EOL
204
- 🤖 I attempted to address the review feedback, but no modifications were made.
205
-
206
- ## Aider Output
207
- \`\`\`
208
- $(cat .github/aider/aider-output.txt || echo 'No output available')
209
- \`\`\`
210
-
211
- Please review the output and provide additional guidance if needed.
212
- EOL
213
- fi
76
+ BASE_PROMPT="Fix the following issues in the PR based on the review feedback. The review body is prepended with REVIEW. The review comments are prepended with REVIEW_COMMENTS. The review body and comments are separated by a blank line."
214
77
215
- # Use the file for comment body
216
- gh pr comment $PR_NUM --body-file /tmp/pr-comment.md
78
+ COMPLETE_PROMPT="${BASE_PROMPT}"$'\n'"REVIEW:"$'\n'"${REVIEW_BODY_ESCAPED}"$'\n'"REVIEW_COMMENTS:"$'\n'"${FORMATTED_COMMENTS}"
79
+
80
+ echo "prompt_content<<EOF" >> $GITHUB_OUTPUT
81
+ echo "$COMPLETE_PROMPT" >> $GITHUB_OUTPUT
82
+ echo "EOF" >> $GITHUB_OUTPUT
83
+
84
+ run-aider :
85
+ needs : [check-membership, check-and-prepare]
86
+ if : github.event.review.state == 'changes_requested' && contains(github.event.pull_request.title, '[Aider PR]') && needs.check-membership.outputs.is_member == 'true'
87
+ uses : windmill-labs/windmill/.github/workflows/aider-common.yml@main
88
+ with :
89
+ needs_processing : false
90
+ base_prompt : ${{ needs.check-and-prepare.outputs.prompt_content }}
91
+ rules_files : ' CLAUDE.md'
92
+ secrets : inherit
0 commit comments