Skip to content

Feat 310 translate GitHub issue bot to python#347

Open
zeus2611 wants to merge 4 commits intoappwrite:mainfrom
zeus2611:feat-310-translate-github-issue-bot-to-python
Open

Feat 310 translate GitHub issue bot to python#347
zeus2611 wants to merge 4 commits intoappwrite:mainfrom
zeus2611:feat-310-translate-github-issue-bot-to-python

Conversation

@zeus2611
Copy link
Contributor

@zeus2611 zeus2611 commented Feb 7, 2026

What does this PR do?

Adds a new Python template for a GitHub Issue Bot that automatically responds to newly opened issues on a GitHub repository. This is a Python port of the existing Node.js github-issue-bot template.

Test Plan

The function was manually deployed to an Appwrite instance and tested end-to-end:

  1. Deployment: Created a new Python 3.9 function in Appwrite Console
  2. Configuration: Set GITHUB_TOKEN and GITHUB_WEBHOOK_SECRET environment variables
  3. Webhook Setup: Configured GitHub repository webhook pointing to the function domain
  4. Integration Test: Created a new issue in the repository
  5. Verification: Confirmed the bot posted a comment on the issue
Screenshot 2026-02-07 at 2 16 14 PM Screenshot 2026-02-07 at 2 16 07 PM

Related PRs and Issues

#310

Have you read the Contributing Guidelines on issues?

Yes, I have read and followed the contributing guidelines.

Summary by CodeRabbit

  • New Features

    • Added a Python GitHub Issue Bot that automatically posts comments on newly opened GitHub issues via webhooks.
    • Added Go as a supported language in the template availability matrix.
  • Documentation

    • Added comprehensive documentation for the GitHub Issue Bot including configuration, environment variables, and webhook specifications.
  • Chores

    • Added project configuration and Python dependencies.

@coderabbitai
Copy link

coderabbitai bot commented Feb 7, 2026

Walkthrough

This pull request introduces a new Python-based GitHub Issue Bot feature. The addition includes a webhook handler (main.py), a service class (github.py) for GitHub API interactions and webhook signature verification, utility functions for environment variable validation, configuration documentation, and standard project files (.gitignore, requirements.txt, README.md). The bot validates incoming GitHub webhooks using HMAC-SHA256, identifies issue-opened events, and posts automated comments to newly opened issues using the PyGithub library. The main table in the root README.md is reorganized to reorder language columns and add a Go language column across all rows.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: translating the GitHub issue bot from Node.js to Python, which is confirmed by the new Python template files and structure added across the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@python/github-issue-bot/src/github.py`:
- Around line 39-44: The current logic that re-serializes a parsed payload_body
dict (the if isinstance(payload_body, dict) branch) breaks GitHub webhook HMAC
verification because the raw HTTP body bytes must be used; change the code to
obtain and use the raw request bytes (prefer a runtime-provided
raw_body/rawBody/bodyBinary if available) before any JSON parsing, remove or
replace the dict re-serialization branch with an explicit failure/error path if
only a parsed dict is available, and update any logging that uses print() (e.g.,
the call referenced around line 88) to use context.error() for consistent
logging; target the payload_body handling in github.py and the related signature
verification call sites when making these changes.

In `@python/github-issue-bot/src/utils.py`:
- Line 19: The environment-variable presence check in the conditional that
currently reads "if key not in obj or obj[key] is None or obj[key] == 0:" is
incorrect because environment values are strings and empty strings should be
treated as falsy; update that conditional in utils.py to test for
falsy/empty-string values (for example use "if key not in obj or not obj[key]"
or explicitly check "obj[key] == ''") so blank environment variables are
rejected; ensure you modify the exact conditional expression where obj[key] is
tested to cover empty string and other falsy cases.

In `@README.md`:
- Line 24: Update the README table row for "Github Issue Bot" to correct the
title to "GitHub Issue Bot" and replace the Python column's placeholder emoji
(🏗️) with a link to the new Python template directory (e.g.,
python/github-issue-bot), mirroring the link format used for other languages;
edit the table cell in the same row as "node/github-issue-bot" so it reads
[✅](python/github-issue-bot) and ensure "Github" is capitalized as "GitHub" in
that row.
🧹 Nitpick comments (4)
python/github-issue-bot/requirements.txt (1)

1-1: Consider pinning the PyGithub version.

An unpinned dependency can lead to non-reproducible builds and unexpected breakage if a new major version introduces breaking changes. Consider pinning to the version you tested with (e.g., PyGithub==2.x.y).

python/github-issue-bot/README.md (1)

15-15: Sample signature value doesn't match the expected format.

The x-hub-signature-256 header from GitHub follows the format sha256=<hex-digest>. The sample value h74ba0jbla01lagudfo is misleading. Consider using a realistic placeholder like sha256=abc123....

python/github-issue-bot/src/main.py (1)

23-26: Duplicate body parsing.

The body is already parsed inside is_issue_opened_event (github.py Line 64-67). Consider having a single parsing step earlier and passing the parsed payload to both methods, or having is_issue_opened_event accept the payload directly.

python/github-issue-bot/src/github.py (1)

75-93: post_comment silently swallows missing data — use context.log/context.error for consistency.

Line 88 uses print() while the rest of the codebase (main.py) uses context.log()/context.error(). Also, silently returning on missing data makes debugging harder — the caller in main.py won't know the comment wasn't posted.

Consider either raising an exception (which main.py already catches) or accepting context to log properly.

Comment on lines +39 to +44
if isinstance(payload_body, dict):
payload_bytes = json.dumps(payload_body, separators=(',', ':')).encode('utf-8')
elif isinstance(payload_body, str):
payload_bytes = payload_body.encode('utf-8')
else:
payload_bytes = payload_body
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*.js" -o -name "*.ts" | grep -i "github-issue-bot" | head -20

Repository: appwrite/templates

Length of output: 393


🏁 Script executed:

git ls-files | grep -E "(node|javascript|typescript)" | grep -i "github-issue-bot"

Repository: appwrite/templates

Length of output: 837


🏁 Script executed:

find . -type f \( -name "*.py" -o -name "*.js" -o -name "*.ts" \) | grep -i "github-issue-bot" | head -30

Repository: appwrite/templates

Length of output: 510


🏁 Script executed:

cat -n python/github-issue-bot/src/github.py

Repository: appwrite/templates

Length of output: 3647


🏁 Script executed:

cat -n python/github-issue-bot/src/main.py

Repository: appwrite/templates

Length of output: 1613


🏁 Script executed:

cat -n node/github-issue-bot/src/github.js

Repository: appwrite/templates

Length of output: 1400


🏁 Script executed:

cat -n node/github-issue-bot/src/main.js

Repository: appwrite/templates

Length of output: 1010


🏁 Script executed:

cat -n node-typescript/github-issue-bot/src/github.ts

Repository: appwrite/templates

Length of output: 1540


🏁 Script executed:

find python/github-issue-bot -type f \( -name "*test*" -o -name "*spec*" \) | head -20

Repository: appwrite/templates

Length of output: 44


🏁 Script executed:

ls -la python/github-issue-bot/

Repository: appwrite/templates

Length of output: 417


🏁 Script executed:

find . -path "*/python/github-issue-bot*" -type f

Repository: appwrite/templates

Length of output: 294


Re-serializing a parsed dict will break webhook signature verification.

GitHub signs the raw HTTP request body. The Node.js reference implementation (using @octokit/webhooks-methods) correctly verifies against req.bodyBinary, while the Python code attempts to handle a parsed dict via json.dumps(separators=(',', ':')). This will not reproduce the original bytes (whitespace, key order, Unicode escapes, etc.), causing HMAC verification to always fail if the Appwrite Python runtime ever delivers context.req.body as a parsed dict.

You should capture the raw body bytes before any JSON parsing. If the Appwrite Python runtime provides a rawBody or bodyBinary equivalent, use that. If the body is always a string in practice, at minimum document that assumption and remove the dict branch (which gives a false sense of safety).

Additionally, line 88 uses print() instead of context.error() for consistency with the logging pattern in main.py.

Proposed fix — prefer raw body, fail explicitly for dicts
         payload_body = context.req.body
 
         if not payload_body:
              return False
-        
-        if isinstance(payload_body, dict):
-            payload_bytes = json.dumps(payload_body, separators=(',', ':')).encode('utf-8')
-        elif isinstance(payload_body, str):
+
+        if isinstance(payload_body, dict):
+            # Cannot reliably verify signature against a re-serialized dict.
+            # Use context.req.bodyRaw or equivalent if available.
+            raise ValueError("Cannot verify webhook signature: raw body is required, but received parsed dict")
+        elif isinstance(payload_body, str):
             payload_bytes = payload_body.encode('utf-8')
         else:
             payload_bytes = payload_body
🤖 Prompt for AI Agents
In `@python/github-issue-bot/src/github.py` around lines 39 - 44, The current
logic that re-serializes a parsed payload_body dict (the if
isinstance(payload_body, dict) branch) breaks GitHub webhook HMAC verification
because the raw HTTP body bytes must be used; change the code to obtain and use
the raw request bytes (prefer a runtime-provided raw_body/rawBody/bodyBinary if
available) before any JSON parsing, remove or replace the dict re-serialization
branch with an explicit failure/error path if only a parsed dict is available,
and update any logging that uses print() (e.g., the call referenced around line
88) to use context.error() for consistent logging; target the payload_body
handling in github.py and the related signature verification call sites when
making these changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant