Writing pull request descriptions can be tedious, especially when you're focused on code. In this tutorial, you'll set up a GitHub Action that uses an AI agent (powered by OpenAI's GPT-4) to automatically generate a PR description from the diff. This workflow runs whenever a PR is opened or updated, saving you time and ensuring consistency.

Prerequisites

  • Basic knowledge of GitHub Actions and YAML
  • Python 3.9+ installed locally (for testing)
  • An OpenAI API key (set as a repository secret)
  • A GitHub repository with write access
Note: This workflow uses OpenAI's GPT-4 model. Adjust the model name and token limits based on your budget and needs.

Step 1: Create the Python Script

Create a file .github/scripts/generate_pr_description.py with the following code. It fetches the diff from the PR event, sends it to the AI, and writes the generated description to a file that the action will use.

import os
import json
import requests

# Get environment variables
diff = os.environ.get("DIFF", "")
api_key = os.environ.get("OPENAI_API_KEY")

if not diff or not api_key:
    print("Missing DIFF or OPENAI_API_KEY")
    exit(1)

prompt = f"""You are an expert code reviewer. Generate a concise pull request description from the following diff.
The description should include:
- Summary of changes
- Key modified files and their purpose
- Any potential impacts or considerations

Diff:
{diff[:6000]}
"""  # Truncate to avoid token limits

headers = {
    "Authorization": f"Bearer {api_key}",
    "Content-Type": "application/json"
}

data = {
    "model": "gpt-4",
    "messages": [
        {"role": "user", "content": prompt}
    ],
    "max_tokens": 500,
    "temperature": 0.3
}

response = requests.post(
    "https://api.openai.com/v1/chat/completions",
    headers=headers,
    json=data
)
response.raise_for_status()

description = response.json()["choices"][0]["message"]["content"]

# Save to file for later use
with open("/tmp/pr_description.txt", "w") as f:
    f.write(description)

print("Description generated successfully")

Step 2: Create the GitHub Action Workflow

Create .github/workflows/auto-pr-description.yml:

name: Auto PR Description

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  generate-description:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Needed to get full diff

      - name: Get diff
        id: diff
        run: |
          git diff origin/${{ github.base_ref }}...${{ github.head_ref }} > /tmp/diff.txt
          cat /tmp/diff.txt
        shell: bash

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.10'

      - name: Install dependencies
        run: pip install requests

      - name: Generate PR description
        id: ai
        env:
          DIFF: ${{ steps.diff.outputs.diff_content }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: python .github/scripts/generate_pr_description.py

      - name: Update PR description
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          DESCRIPTION=$(cat /tmp/pr_description.txt)
          gh pr edit ${{ github.event.number }} --body "$DESCRIPTION"
Warning: The first step tries to capture the diff content into an output variable, but the command inside run does not set outputs correctly. In the next step we'll fix this with a proper method.

Step 3: Fix the Diff Capture

The get diff step above will not save the diff to an output variable as written. Use this corrected version instead:

      - name: Get diff
        id: diff
        run: |
          DIFF_CONTENT=$(git diff origin/${{ github.base_ref }}...${{ github.head_ref }})
          echo "diff_content<> $GITHUB_OUTPUT
          echo "$DIFF_CONTENT" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT
        shell: bash

Then update the Generate PR description step to use ${{ steps.diff.outputs.diff_content }} as the DIFF environment variable.

Step 4: Add the OpenAI API Key as a Secret

Go to your repository → Settings → Secrets and variables → Actions → New repository secret. Name it OPENAI_API_KEY and paste your key.

Step 5: Test the Workflow

Create a new branch, make a meaningful change, and open a PR. After a few seconds, you should see the PR description updated automatically with the AI-generated content.

Success! You now have an automated PR description generator. You can customize the prompt in the Python script to fit your team’s style (e.g., include issue references, changelog format, or reviewer notes).

Next Steps & Improvements

  • Add error handling: if the API call fails, fall back to a default message.
  • Use a cheaper model (e.g., gpt-3.5-turbo) for smaller diffs.
  • Include structured labels: append “AI-generated: review required”.
  • Integrate with your ticket system by parsing branch names (e.g., JIRA-123-feature).

This workflow saves at least 5 minutes per PR and ensures consistent descriptions across your team. Happy coding! 🚀