From 518906f3cc367d602174b5df742a7f180a92b093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Fri, 10 Nov 2023 20:16:32 +0100 Subject: [PATCH] ci: started working on composite action for gptme-bot, generated basic docs --- .github/workflows/bot-composite.yml | 243 ++++++++++++++++++++++++++++ docs/bot.md | 53 ++++++ 2 files changed, 296 insertions(+) create mode 100644 .github/workflows/bot-composite.yml create mode 100644 docs/bot.md diff --git a/.github/workflows/bot-composite.yml b/.github/workflows/bot-composite.yml new file mode 100644 index 00000000..970352c2 --- /dev/null +++ b/.github/workflows/bot-composite.yml @@ -0,0 +1,243 @@ +name: 'gptme-bot' +description: 'A composite action for the gptme-bot workflow' +inputs: + openai_api_key: + description: 'OpenAI API Key' + required: true + github_token: + description: 'GitHub Token' + required: true + issue_number: + description: 'Issue Number' + required: true + comment_body: + description: 'Comment Body' + required: true + comment_id: + description: 'Comment ID' + required: true + repo_name: + description: 'Repository Name' + required: true + user_name: + description: 'User Name' + required: true + branch_base: + description: 'Base Branch' + required: true + python_version: + description: 'Python Version' + required: true + is_pr: + description: 'Is Pull Request' + required: true + branch_name: + description: 'Branch Name' + required: true +runs: + using: "composite" + steps: + - name: Detect gptme command + id: detect_command + run: | + # Check if the comment starts with "@gptme" + if [[ "${{ inputs.comment_body }}" == "@gptme "* ]]; then + # Extract the command + GPTME_COMMAND=${{ inputs.comment_body }}#"@gptme " + # Escape double quotes + # GPTME_COMMAND="${GPTME_COMMAND//\"/\\\"}" + # Set output + { + echo "gptme_command<> $GITHUB_OUTPUT + fi + shell: bash + + - name: Fail if author not on whitelist + if: steps.detect_command.outputs.gptme_command + run: | + WHITELIST=("ErikBjare") # Add your allowed usernames here + COMMENT_AUTHOR="${{ inputs.user_name }}" + + if [[ ! " ${WHITELIST[@]} " =~ " ${COMMENT_AUTHOR} " ]]; then + echo "Error: Author ${COMMENT_AUTHOR} is not on the whitelist." + exit 1 + else + echo "Author ${COMMENT_AUTHOR} is on the whitelist." + fi + shell: bash + + - name: React to comment + if: steps.detect_command.outputs.gptme_command + run: | + gh api /repos/${{ inputs.user_name }}/${{ inputs.repo_name }}/issues/comments/${{ inputs.comment_id }}/reactions -X POST -f content='+1' + shell: bash + env: + GITHUB_TOKEN: ${{ inputs.github_token }} + + - name: Checkout + uses: actions/checkout@v3 + + - name: Install ctags + run: sudo apt install universal-ctags + shell: bash + + - name: Checkout PR branch if comment is on a PR + id: checkout_branch + run: | + # Fetch details about the "issue" the comment is on + DATA=$(gh api /repos/${{ github.repository }}/issues/${{ inputs.issue_number }}) + + # Extract whether this "issue" is a PR and + IS_PR=${{ inputs.is_pr }} + + # If this is a PR, checkout its branch + if [[ "$IS_PR" != "null" ]]; then + # Fetch details about the PR the comment is on + DATA=$(gh api /repos/${{ github.repository }}/pulls/${{ inputs.issue_number }}) + + # Get the PR's branch name + BRANCH_NAME=${{ inputs.branch_name }} + git fetch origin $BRANCH_NAME + git checkout $BRANCH_NAME + else + # Create a new branch and push changes + BRANCH_NAME="gptme-bot-changes-$(date +'%Y%m%d%H%M%S')" + git checkout -b $BRANCH_NAME + fi + echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT + shell: bash + env: + GITHUB_TOKEN: ${{ inputs.github_token }} + + - name: Install poetry + run: pipx install poetry + shell: bash + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ inputs.python_version }} + cache: 'poetry' + + - name: Install dependencies + run: | + make build + poetry install -E datascience + shell: bash + + - name: Run gptme + run: | + gh issue view ${{ inputs.issue_number }} > issue.md + gh issue view ${{ inputs.issue_number }} -c > comments.md + + # strip long
...
from issue.md and comments.md + perl -0777 -i -pe 's/\n
.*?<\/details>//sg' issue.md + perl -0777 -i -pe 's/\n
.*?<\/details>//sg' comments.md + + # install a shim that makes `git commit` a no-op (in case it would get that idea prematurely) + source scripts/git-shim.sh + + # Run gptme with the extracted command and save logs + poetry run gptme --non-interactive "$GPTME_COMMAND" issue.md comments.md + + # remove tmp files so that they do not get committed + rm issue.md comments.md + + # stage changes + git add -A + shell: bash + env: + GITHUB_TOKEN: ${{ inputs.github_token }} + GPTME_COMMAND: ${{ steps.detect_command.outputs.gptme_command }} + ISSUE_NUMBER: ${{ inputs.issue_number }} + + - name: Generate commit message + run: | + # generate commit message + poetry run gptme --non-interactive "Run `git diff --staged`, then write a commit message for it to message.txt, following conventional commits. Don't commit." + shell: bash + + - name: Commit, push, comment + run: | + # 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/${{ inputs.issue_number }}#issuecomment-${{ inputs.comment_id }}" + + # commit message & description + COMMIT_MSG="$(cat message.txt || echo 'no commit message')" + COMMIT_DESC="\`gptme '$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 + +
+ Log +
$(cat log.txt || echo 'could not get log')
+
" + + git config user.name "gptme-bot" + git config user.email "gptme-bot@superuserlabs.org" + + git commit -m "$COMMIT_MSG_FULL" + + # Push changes to the PR branch + git push -u origin $BRANCH_NAME + + if [[ $ISSUE_TYPE == "pull_request" ]]; then + # Comment on the PR + echo "Changes have been pushed to this pull request." | gh pr comment $ISSUE_NUMBER -R $USER_NAME/$REPO_NAME --body-file=- + else + # Some say this helps! https://github.com/cli/cli/issues/2691#issuecomment-1289521962 + sleep 1 + + # Create a PR + PR_URL=$(gh pr create --title "$COMMIT_MSG" --body "$COMMIT_MSG_FULL_WITH_LOG" --repo $USER_NAME/$REPO_NAME | grep -o 'https://github.com[^ ]*') + # These are redundant/implied: --base $BRANCH_BASE --head $USER_NAME:$BRANCH_NAME + + # Comment on the issue with the PR link + echo "A pull request has been created for this issue: $PR_URL" | gh issue comment $ISSUE_NUMBER -R $USER_NAME/$REPO_NAME --body-file=- + fi + shell: bash + env: + GITHUB_TOKEN: ${{ inputs.github_token }} + GPTME_COMMAND: ${{ steps.detect_command.outputs.gptme_command }} + ISSUE_NUMBER: ${{ inputs.issue_number }} + ISSUE_TYPE: ${{ github.event.issue.pull_request && 'pull_request' || 'issue' }} + REPO_NAME: ${{ inputs.repo_name }} + USER_NAME: ${{ inputs.user_name }} + BRANCH_NAME: ${{ steps.checkout_branch.outputs.branch_name }} + BRANCH_BASE: ${{ inputs.branch_base }} + + - name: Report error + if: failure() + run: | + # reply to the comment that we could not fulfill the request + RUN_URL="https://github.com/${{ 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+=" +
+ Conversation log +
$(cat log.txt)
+
" + fi + echo "$MESSAGE" | gh issue comment $ISSUE_NUMBER -R $USER_NAME/$REPO_NAME --body-file=- + shell: bash + env: + GITHUB_TOKEN: ${{ inputs.github_token }} + ISSUE_NUMBER: ${{ inputs.issue_number }} + REPO_NAME: ${{ inputs.repo_name }} + USER_NAME: ${{ inputs.user_name }} diff --git a/docs/bot.md b/docs/bot.md new file mode 100644 index 00000000..a5c4e422 --- /dev/null +++ b/docs/bot.md @@ -0,0 +1,53 @@ +GitHub Bot +========== + +One way to run gptme is as a GitHub bot. + +The `gptme-bot` composite action is a GitHub Action that automates the process of running the `gptme` in response to comments on GitHub issues or pull requests. It is designed to be used for tasks that gptme can perform with a one-shot prompt, such as running commands and committing their results, creating files or making simple changes/additions (like write tests), and (potentially) automating code reviews. + +## Inputs + +The action has the following inputs: + +- `openai_api_key`: The OpenAI API key. Required. +- `github_token`: The GitHub token. Required. +- `issue_number`: The number of the issue or pull request the comment is associated with. Required. +- `comment_body`: The body of the comment. Required. +- `comment_id`: The ID of the comment. Required. +- `repo_name`: The name of the repository. Required. +- `user_name`: The username of the comment author. Required. +- `branch_base`: The base branch for the pull request. Required. +- `python_version`: The version of Python to use. Required. +- `is_pr`: A boolean indicating whether the issue is a pull request. Required. +- `branch_name`: The name of the branch associated with the pull request. Required. + +## Usage + +To use the `gptme-bot` composite action in your workflow, include it as a step: + +```yaml +on: + issue_comment: + types: [created] + +steps: + - name: Run gptme-bot composite action + uses: ./.github/actions/bot-composite + with: + openai_api_key: ${{ secrets.OPENAI_API_KEY }} + github_token: ${{ github.token }} + issue_number: ${{ github.event.issue.number }} + comment_body: ${{ github.event.comment.body }} + comment_id: ${{ github.event.comment.id }} + repo_name: ${{ github.event.repository.name }} + user_name: ${{ github.event.repository.owner.login }} + branch_base: "master" + python_version: '3.11' + is_pr: ${{ github.event.issue.pull_request != null }} + branch_name: ${{ github.event.pull_request.head.ref }} +``` + +Please note that you need to replace the `uses` path with the correct path to your `bot-composite.yml` file. Also, make sure to replace the `branch_base`, `python_version`, `is_pr`, and `branch_name` inputs with the correct values for your use case. + +The `gptme-bot` composite action will then run the `gptme` command-line tool with the command specified in the comment, and perform actions based on the output of the tool. This includes checking out the appropriate branch, installing dependencies, running the `gptme` command, and committing and pushing any changes made by the tool. If the issue is a pull request, the action will push changes directly to the pull request branch. If the issue is not a pull request, the action will create a new pull request with the changes. +