-
Notifications
You must be signed in to change notification settings - Fork 0
/
action.yml
315 lines (271 loc) · 11.5 KB
/
action.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
name: 'gptme-bot'
description: 'A composite action for the gptme-bot workflow'
inputs:
openai_api_key:
description: 'OpenAI API Key'
required: false
anthropic_api_key:
description: 'Anthropic API Key'
required: false
model:
description: 'Model to use'
required: false
github_token:
description: 'GitHub Token'
required: true
allowlist:
description: 'Comma-separated list of GitHub usernames allowed to trigger the bot'
required: true
default: 'Sunwood-ai-labs'
runs:
using: "composite"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get PR details
id: pr_details
if: ${{ github.event.issue.pull_request }}
shell: bash
run: |
PR_NUMBER=${{ github.event.issue.number }}
PR_DATA=$(curl -s -H "Authorization: token ${{ inputs.github_token }}" \
"https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER")
echo "head_ref=$(echo "$PR_DATA" | jq -r .head.ref)" >> $GITHUB_OUTPUT
echo "base_ref=$(echo "$PR_DATA" | jq -r .base.ref)" >> $GITHUB_OUTPUT
- name: Detect gptme command
id: detect_command
shell: bash
env:
COMMENT_BODY: ${{ github.event.comment.body }}
run: |
# Check if the comment starts with "@gptme"
if [[ "$COMMENT_BODY" == "@gptme "* ]]; then
# Extract the command
GPTME_COMMAND=${COMMENT_BODY#"@gptme "}
{
echo "gptme_command<<EOFMAGIC"
echo $GPTME_COMMAND
echo "EOFMAGIC"
} >> $GITHUB_OUTPUT
fi
- name: Check allowlist
if: steps.detect_command.outputs.gptme_command
shell: bash
run: |
IFS=',' read -ra ALLOWLIST <<< "${{ inputs.allowlist }}"
if [[ ! " ${ALLOWLIST[@]} " =~ " ${{ github.event.comment.user.login }} " ]]; then
echo "Error: Author ${{ github.event.comment.user.login }} is not on the allowlist."
exit 1
fi
- name: React to comment
if: steps.detect_command.outputs.gptme_command
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.github_token }}
run: |
gh api /repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions -X POST -f content='+1'
- name: Log in to GitHub Container Registry
if: steps.detect_command.outputs.gptme_command
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ inputs.github_token }}
- name: Pull Docker image
if: steps.detect_command.outputs.gptme_command
shell: bash
run: docker pull ghcr.io/erikbjare/gptme:latest
- name: Checkout PR branch if comment is on a PR
id: checkout_branch
if: steps.detect_command.outputs.gptme_command
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.github_token }}
run: |
IS_PR=${{ github.event.issue.pull_request != null }}
BRANCH_NAME=${{ steps.pr_details.outputs.head_ref || format('gptme/bot-changes-{0}', github.run_id) }}
if [[ "$IS_PR" == "true" ]]; then
git fetch origin $BRANCH_NAME
git checkout $BRANCH_NAME
else
git checkout -b $BRANCH_NAME
fi
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
- name: Prepare directories and permissions
shell: bash
run: |
mkdir -p ~/.config/gptme
mkdir -p ~/.local/share/gptme
chmod -R 777 ~/.config/gptme
chmod -R 777 ~/.local/share/gptme
- name: Write gptme config.toml
shell: bash
run: |
mkdir -p ~/.config/gptme
cat > ~/.config/gptme/config.toml << EOF
[prompt]
about_user = "The user is talking to the gptme bot through GitHub comments. They have initiated conversation by pinging it with @gptme, triggering a GitHub Action in which gptme now runs."
response_preference = "Don't explain basic concepts"
[env]
MODEL = "${{ inputs.model }}"
OPENAI_API_KEY = "${{ inputs.openai_api_key }}"
ANTHROPIC_API_KEY = "${{ inputs.anthropic_api_key }}"
EOF
- name: Determine action type
id: determine_action
if: steps.detect_command.outputs.gptme_command
shell: bash
env:
GPTME_COMMAND: ${{ steps.detect_command.outputs.gptme_command }}
run: |
FULL_OUTPUT=$(docker run --rm \
-v ~/.config/gptme:/home/appuser/.config/gptme \
ghcr.io/erikbjare/gptme:latest \
--non-interactive \
"Determine if this command requires changes to be made or just a response. Respond with either 'make_changes' or 'respond'. Command: $GPTME_COMMAND")
# Extract the last line, which should be the actual response
ACTION_TYPE=$(echo "$FULL_OUTPUT" | grep -oE '(make_changes|respond)' | tail -n 1)
echo "action_type=$ACTION_TYPE" >> $GITHUB_OUTPUT
# Validate the action type
if [[ "$ACTION_TYPE" != "make_changes" && "$ACTION_TYPE" != "respond" ]]; then
echo "Error: Invalid action type '$ACTION_TYPE'. Expected 'make_changes' or 'respond'."
echo "Full output was:"
echo "$FULL_OUTPUT"
exit 1
fi
- name: Get context
id: context
if: steps.detect_command.outputs.gptme_command
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.github_token }}
run: |
# download shorten_details script
SCRIPTDIR=$(mktemp -d)
curl -s https://raw.githubusercontent.com/ErikBjare/gptme/master/scripts/shorten_details.py > $SCRIPTDIR/shorten_details.py
chmod +x $SCRIPTDIR/shorten_details.py
NUM=${{ github.event.issue.number }}
CTXDIR=$(mktemp -d)
# if issue
if [[ ${{ github.event.issue.pull_request == null }} == "true" ]]; then
gh issue view $NUM | $SCRIPTDIR/shorten_details.py 1000 > $CTXDIR/gh-issue.md
gh issue view $NUM -c | $SCRIPTDIR/shorten_details.py 1000 > $CTXDIR/gh-comments.md
# if PR
else
gh pr view $NUM | $SCRIPTDIR/shorten_details.py 1000 > $CTXDIR/gh-pr.md
gh pr view $NUM -c | $SCRIPTDIR/shorten_details.py 1000 > $CTXDIR/gh-comments.md
gh pr diff $NUM > $CTXDIR/gh-diff.md
gh pr checks $NUM > $CTXDIR/gh-checks.md
fi
# make sure anyone (even docker) can read/write the context dir
chmod -R o=rwx $CTXDIR
# output context dir
echo "dir=$CTXDIR" >> $GITHUB_OUTPUT
- name: Run gptme
if: steps.detect_command.outputs.gptme_command
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.github_token }}
GPTME_COMMAND: ${{ steps.detect_command.outputs.gptme_command }}
run: |
echo "Running in $GITHUB_WORKSPACE"
CTXDIR=${{ steps.context.outputs.dir }}
# make sure anyone (even docker) can read/write the workspace
chmod -R o=rwx .
# run gptme with the extracted command and save logs
# TODO: actually move the <system> tag into a system message
timeout 120 docker run --rm \
-v ~/.config/gptme:/home/appuser/.config/gptme \
-v $GITHUB_WORKSPACE:/workspace \
-v $CTXDIR:$CTXDIR \
-w /workspace \
ghcr.io/erikbjare/gptme:latest \
--non-interactive \
"$GPTME_COMMAND" \
"<system>" \
"The project has been cloned to the current directory and can be explored with git, cat, etc." \
"Here is the context:" $CTXDIR/* \
"</system>" \
"-" "Write the response to 'response.md', it will be posted as a comment." \
|| (echo "TIMEOUT" && exit 1)
# comment with the response
if [[ -f response.md ]]; then
gh issue comment ${{ github.event.issue.number }} -R ${{ github.repository }} --body-file=response.md
else
echo "No response was generated."
fi
# stage changes
git add -A
- name: Prepare script permissions
if: steps.detect_command.outputs.gptme_command
shell: bash
run: |
# Ensure scripts directory exists
mkdir -p scripts
# Download format_log.sh if it doesn't exist
if [ ! -f ./scripts/format_log.sh ]; then
curl -o ./scripts/format_log.sh https://raw.githubusercontent.com/ErikBjare/gptme/master/scripts/format_log.sh
fi
# Make script executable
chmod +x ./scripts/format_log.sh
- name: Commit, push, comment
if: steps.detect_command.outputs.gptme_command && steps.determine_action.outputs.action_type == 'make_changes'
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.github_token }}
run: |
# generate commit message
docker run --rm \
-v ~/.config/gptme:/home/appuser/.config/gptme \
-v $GITHUB_WORKSPACE:/workspace \
-w /workspace \
ghcr.io/erikbjare/gptme:latest \
--non-interactive \
"Run 'git diff --staged' to inspect what has changed." \
"-" "Write a commit message for it to 'message.txt'. Use the 'conventional commits' style."
# Read and format log
./scripts/format_log.sh ~/.local/share/gptme/logs/*/conversation.jsonl > log.txt
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
COMMENT_URL="https://github.com/${{ github.repository }}/issues/${{ github.event.issue.number }}#issuecomment-${{ github.event.comment.id }}"
# commit message & description
COMMIT_MSG="$(cat message.txt || echo 'no commit message')"
COMMIT_DESC="\`gptme '${{ steps.detect_command.outputs.gptme_command }}'\`
Triggered by: $COMMENT_URL
Run: $RUN_URL"
# commit message with description
COMMIT_MSG_FULL="$COMMIT_MSG
$COMMIT_DESC"
# commit message with description and log
COMMIT_MSG_FULL_WITH_LOG="$COMMIT_MSG_FULL
<details>
<summary>Log</summary>
<pre>$(cat log.txt || echo 'could not get log')</pre>
</details>"
git config user.name "gptme-bot"
git config user.email "gptme-bot@superuserlabs.org"
git commit -m "$COMMIT_MSG_FULL"
git push -u origin ${{ steps.checkout_branch.outputs.branch_name }}
if [[ ${{ github.event.issue.pull_request != null }} == "true" ]]; then
echo "Changes have been pushed to this pull request." | gh pr comment ${{ github.event.issue.number }} -R ${{ github.repository }} --body-file=-
else
# Some say this helps! https://github.com/cli/cli/issues/2691#issuecomment-1289521962
sleep 1
PR_URL=$(gh pr create --title "$COMMIT_MSG" --body "$COMMIT_MSG_FULL_WITH_LOG" --repo ${{ github.repository }} | grep -o 'https://github.com[^ ]*')
echo "A pull request has been created for this issue: $PR_URL" | gh issue comment ${{ github.event.issue.number }} -R ${{ github.repository }} --body-file=-
fi
- name: Report error
if: failure()
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.github_token }}
run: |
RUN_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
MESSAGE="I'm sorry, I could not fulfill your request. Please check the [log of this run]($RUN_URL) for more information."
if [[ -f log.txt ]]; then
MESSAGE+="
<details>
<summary>Conversation log</summary>
<pre>$(cat log.txt)</pre>
</details>"
fi
echo "$MESSAGE" | gh issue comment ${{ github.event.issue.number }} -R ${{ github.repository }} --body-file=-