Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
0651b00
feat(graphql): complete migration from REST to GraphQL API v4
myakove Oct 20, 2025
74100a1
fix: address 17 CodeRabbit review comments from GraphQL migration
myakove Oct 29, 2025
58e47e9
fix: address 7 CodeRabbit review comments
myakove Oct 29, 2025
a0e5987
fix: address 3 CodeRabbit review comments from PR#878
myakove Oct 30, 2025
8e837c3
test: add edge case tests and fix for recursive GraphQL tree
myakove Oct 30, 2025
4d3ed24
fix: resolve GraphQL query depth exceeded error and add config dot no…
myakove Oct 30, 2025
1a56c13
refactor: fix ruff ARG errors and prettier version
myakove Oct 30, 2025
283057a
fix: address CodeRabbit review comments from PR#878
myakove Oct 30, 2025
f83fccc
fix: reduce GraphQL tree depth and enable SSL verification
myakove Oct 30, 2025
66e0ecd
fix: address CodeRabbit review comments from PR#878
myakove Oct 30, 2025
cf44322
fix: address CodeRabbit review comments from PR#878
myakove Oct 30, 2025
1f430e0
fix: address CodeRabbit review comments from PR#878
myakove Oct 30, 2025
8ddf85c
feat: add configurable GraphQL query limits and standardize defaults
myakove Oct 30, 2025
0116474
test: update unified_api tests for configurable GraphQL query limits
myakove Oct 30, 2025
bd3e00a
fix: improve GraphQL timeout handling and fix config overwrite issue
myakove Oct 30, 2025
f001d8a
fix: RUN systemd-machine-id-setup in Dockerfile
myakove Oct 30, 2025
ecca2f1
STDIN
myakove Oct 31, 2025
aa604b3
address coderabbit comments
myakove Oct 31, 2025
ff23ba9
Fix node_id > id from GQLdata for labels
myakove Oct 31, 2025
a54e190
fix the tests
myakove Oct 31, 2025
dcea152
Fix Commiter fetch
myakove Oct 31, 2025
9aa4bf4
Address the rabbit code
myakove Oct 31, 2025
fbbf4e6
gql client add retry for comnnection errors
myakove Oct 31, 2025
ba6bc1b
address coderabbit comments
myakove Oct 31, 2025
c729afa
test: improve test coverage and cleanup edge cases
myakove Oct 31, 2025
895f8b6
docs: improve and add missing docstrings across codebase
myakove Nov 1, 2025
ce800a8
fix: hadle labels always with frash pr data
myakove Nov 1, 2025
4b9a72a
fix: Some tests tried to access the outside network, mock them
myakove Nov 1, 2025
0f42bd6
fix: in wait_fol_label, always fetch PR data before check if label ex…
myakove Nov 1, 2025
3414fcd
fix: in wait_fol_label, always fetch PR data before check if label ex…
myakove Nov 1, 2025
6926a0c
fix: in wait_fol_label, always fetch PR data before check if label ex…
myakove Nov 1, 2025
742e384
fix: in wait_fol_label, always fetch PR data before check if label ex…
myakove Nov 1, 2025
1fa9a23
test: add coverage for last_commit=None case in check_if_can_be_merged
myakove Nov 2, 2025
f3f2673
fix: improve GraphQL error handling and use fresh PR data
myakove Nov 2, 2025
52a5699
Fix mergeable state handling in GraphQL API
myakove Nov 2, 2025
2f402ec
Optimize check_run webhook PR lookup and fix edit_issue dict handling
myakove Nov 2, 2025
c3a36b7
Remove max-owners-files limit functionality
myakove Nov 3, 2025
1a8d984
Add GraphQL debugger script and enhance query logging
myakove Nov 3, 2025
71312ca
Fix webhook timeout: return 200 immediately and log all bot selections
myakove Nov 4, 2025
41c5357
Fix test: update error message assertion for missing repository
myakove Nov 4, 2025
fa40a0d
Add tests to reach 90% coverage threshold
myakove Nov 4, 2025
05c25d2
app entry, add repo name to log_context
myakove Nov 5, 2025
1a0550d
add the expection to the log if process failed
myakove Nov 5, 2025
a8ab57e
Fix exception handling and improve test coverage to 90.10%
myakove Nov 5, 2025
4ea56b6
Fix PR review comments: GraphQL repository data and error handling
myakove Nov 5, 2025
06d6630
Add background task tracking for observability and graceful shutdown
myakove Nov 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ coverage.xml
.hypothesis/
.pytest_cache/
cover/
coverage.json


# Translations
*.mo
Expand Down Expand Up @@ -150,6 +152,8 @@ webhook-server.private-key.pem
log-colors.json
webhook_server/tests/manifests/logs
.coverage_report.txt
webhook-examples
find_unused_code.py

# AI
.cursor/
Expand Down
10 changes: 10 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,13 @@ repos:
- id: mypy
exclude: (tests/)
additional_dependencies: [types-requests, types-PyYAML, types-colorama]

- repo: https://github.com/pre-commit/mirrors-eslint
rev: v9.38.0
hooks:
- id: eslint
files: \.js$
exclude: eslint\.config\.js
args: [--fix]
additional_dependencies:
- eslint@9.38.0
11 changes: 9 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ ENV PATH="$PATH:$BIN_DIR"
ENV DATA_DIR="$HOME_DIR/data"
ENV APP_DIR="$HOME_DIR/github-webhook-server"

RUN systemd-machine-id-setup

RUN dnf -y install dnf-plugins-core \
&& dnf -y update \
&& dnf -y install \
git \
hub \
unzip \
gcc \
python3-devel \
Expand Down Expand Up @@ -51,6 +52,7 @@ ENV UV_PYTHON=python3.13
ENV UV_COMPILE_BYTECODE=1
ENV UV_NO_SYNC=1
ENV UV_CACHE_DIR=${APP_DIR}/.cache
ENV PYTHONUNBUFFERED=1

COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx ${BIN_DIR}/
RUN uv tool install pre-commit && uv tool install poetry && uv tool install prek
Expand All @@ -62,7 +64,12 @@ RUN set -x \
&& chmod +x $BIN_DIR/rosa \
&& rm -rf $BIN_DIR/rosa-linux.tar.gz \
&& curl -L https://github.com/regclient/regclient/releases/latest/download/regctl-linux-amd64 >$BIN_DIR/regctl \
&& chmod +x $BIN_DIR/regctl
&& chmod +x $BIN_DIR/regctl \
&& curl -L https://github.com/mislav/hub/releases/download/v2.14.2/hub-linux-amd64-2.14.2.tgz --output ${BIN_DIR}/hub-linux-amd64.tgz \
&& tar xvf ${BIN_DIR}/hub-linux-amd64.tgz \
&& mv hub-linux-amd64-2.14.2/bin/hub ${BIN_DIR}/hub \
&& chmod +x ${BIN_DIR}/hub \
&& rm -rf ${BIN_DIR}/hub-linux-amd64-2.14.2*

WORKDIR $APP_DIR

Expand Down
88 changes: 88 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,17 @@ GitHub Events → Webhook Server → Repository Management
│ • Container Building │
│ • PyPI Publishing │
│ • Code Review Automation │
│ • GraphQL-First API Strategy │
└─────────────────────────────────────┘
```

**Key Architecture Components:**

- **GraphQL-First API**: Optimized GitHub API integration reducing calls by 50-70%
- **Unified API Layer**: Single abstraction for both GraphQL and REST operations
- **Performance Optimized**: Repository data fetched in single comprehensive query per webhook
- **Type-Safe**: Full mypy strict mode coverage with PyGithub-compatible wrappers

## Features

### 🔧 Repository Management
Expand All @@ -65,6 +73,7 @@ GitHub Events → Webhook Server → Repository Management
- **Label management** with automatic creation of missing labels
- **Webhook configuration** with automatic setup and validation
- **Multi-repository support** with centralized configuration
- **GraphQL-optimized API calls** reducing webhook processing latency by up to 70%

### 📋 Pull Request Automation

Expand Down Expand Up @@ -1412,6 +1421,85 @@ mask-sensitive-data: false # Only for debugging - NOT recommended in production

**⚠️ Warning**: Disabling sensitive data masking will expose tokens, passwords, and API keys in logs. Use only in development environments.

### Debug GraphQL Queries

The webhook server includes debug logging for all GraphQL queries executed against the GitHub API. When debug logging is enabled, you can see the exact queries and variables being sent, making it easy to reproduce issues using the debugger script.

#### Using the GraphQL Debugger Script

A debugger script is provided to test GraphQL queries independently:

```bash
# Token can be provided as argument or via $GITHUB_TOKEN environment variable

# Interactive mode - Start a REPL for running queries (token from env)
export GITHUB_TOKEN=ghp_xxxxxxxxxxxxx
uv run scripts/debugger.py

# Interactive mode - Token as argument
uv run scripts/debugger.py ghp_xxxxxxxxxxxxx

# Single query mode - Execute one query and exit (token from env)
uv run scripts/debugger.py --query 'query { viewer { login } }'

# With variables
uv run scripts/debugger.py \
--query 'query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { name } }' \
--variables '{"owner": "myk-org", "name": "github-webhook-server"}'

# Verbose logging
uv run scripts/debugger.py --verbose
```

#### Finding Queries in Logs

When `log-level: DEBUG` is enabled, all GraphQL queries are logged with their variables:

```
DEBUG GraphQL Query:
query($owner: String!, $name: String!) {
repository(owner: $owner, name: $name) {
name
description
}
}
DEBUG GraphQL Variables:
{
"owner": "myk-org",
"name": "github-webhook-server"
}
```

#### Reproducing Issues

1. **Enable debug logging** in your configuration:
```yaml
log-level: DEBUG
```

2. **Trigger the webhook** that causes the issue

3. **Find the query** in the logs (look for `GraphQL Query:`)

4. **Copy the query and variables** from the logs

5. **Test with the debugger script** (token from `$GITHUB_TOKEN` or as argument):
```bash
uv run scripts/debugger.py \
--query '<query from logs>' \
--variables '<variables from logs>'
```

6. **Debug independently** - The script uses the same GraphQL client as the webhook server, so you can test queries in isolation without triggering webhook processing.

#### Features

- **Exact Query Logging**: Full query strings are logged before execution
- **Variable Logging**: Variables are logged as formatted JSON
- **Error Context**: Failed queries include the query and variables in error logs
- **Debugger Script**: Standalone script for testing queries independently
- **Automatic Whitespace Handling**: Variables are automatically stripped of whitespace to prevent common errors

### Log Analysis

Key log patterns to monitor:
Expand Down
3 changes: 3 additions & 0 deletions entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ def run_podman_cleanup() -> None:
run_podman_cleanup()

result = asyncio.run(repository_and_webhook_settings(webhook_secret=_webhook_secret))

# Uvicorn will use default logging which respects FORCE_COLOR environment variable
# Application logs use simple-logger with console=True for colored output
uvicorn.run(
"webhook_server.app:FASTAPI_APP",
host=_ip_bind,
Expand Down
92 changes: 92 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
module.exports = [
// Frontend configuration - for browser-based JavaScript
{
files: ["webhook_server/web/static/**/*.js"],
languageOptions: {
ecmaVersion: 2022,
sourceType: "module",
globals: {
// Browser environment globals
window: "readonly",
document: "readonly",
console: "readonly",
fetch: "readonly",
WebSocket: "readonly",
localStorage: "readonly",
sessionStorage: "readonly",
alert: "readonly",
confirm: "readonly",
prompt: "readonly",
setTimeout: "readonly",
clearTimeout: "readonly",
setInterval: "readonly",
clearInterval: "readonly",
URLSearchParams: "readonly",
AbortController: "readonly",
},
},
rules: {
// ESLint recommended rules (manually specified for broader coverage)
"constructor-super": "error",
"for-direction": "error",
"getter-return": "error",
"no-async-promise-executor": "error",
"no-case-declarations": "error",
"no-class-assign": "error",
"no-compare-neg-zero": "error",
"no-cond-assign": "error",
"no-const-assign": "error",
"no-constant-condition": "error",
"no-control-regex": "error",
"no-debugger": "error",
"no-delete-var": "error",
"no-dupe-args": "error",
"no-dupe-class-members": "error",
"no-dupe-else-if": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-empty": "error",
"no-empty-character-class": "error",
"no-empty-pattern": "error",
"no-ex-assign": "error",
"no-extra-boolean-cast": "error",
"no-fallthrough": "error",
"no-func-assign": "error",
"no-global-assign": "error",
"no-import-assign": "error",
"no-inner-declarations": "error",
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-loss-of-precision": "error",
"no-misleading-character-class": "error",
"no-new-symbol": "error",
"no-obj-calls": "error",
"no-octal": "error",
"no-prototype-builtins": "error",
"no-redeclare": "error",
"no-regex-spaces": "error",
"no-self-assign": "error",
"no-setter-return": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-this-before-super": "error",
"no-unexpected-multiline": "error",
"no-unreachable": "error",
"no-unsafe-finally": "error",
"no-unsafe-negation": "error",
"no-unsafe-optional-chaining": "error",
"no-unused-labels": "error",
"no-useless-backreference": "error",
"no-useless-catch": "error",
"no-useless-escape": "error",
"no-with": "error",
"require-yield": "error",
"use-isnan": "error",
"valid-typeof": "error",
// Project-specific overrides
"no-unused-vars": "warn",
"no-undef": "error",
"no-console": "off",
},
},
];
18 changes: 18 additions & 0 deletions examples/.github-webhook-server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,24 @@ minimum-lgtm: 2
# Issue creation for new pull requests
create-issue-for-new-pr: true # Create tracking issues for new PRs

# GraphQL Query Optimization (optional)
# Customize pagination limits for repository data fetching
# Defaults to 100 for all if not specified
# Maximum: 100 (GitHub GraphQL API limit)
graphql:
query-limits:
collaborators: 100 # Max collaborators to fetch per query
contributors: 100 # Max contributors (mentionableUsers) to fetch
issues: 100 # Max open issues to fetch
pull-requests: 100 # Max open pull requests to fetch
labels: 100 # Max labels to fetch per PR/issue
reviews: 100 # Max reviews to fetch per PR
commits: 100 # Max commits to fetch per PR
comments: 100 # Max comments to fetch per PR
assignees: 100 # Max assignees to fetch per PR
files: 100 # Max changed files to fetch per PR
associated-pull-requests: 100 # Max associated PRs to fetch per commit

# Custom PR size labels for this repository (overrides global configuration)
# Define custom categories based on total lines changed (additions + deletions)
# threshold: positive integer representing minimum lines changed for this category
Expand Down
18 changes: 18 additions & 0 deletions examples/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ auto-verify-cherry-picked-prs: true # Default: true - automatically verify cher

create-issue-for-new-pr: true # Global default: create tracking issues for new PRs

# GraphQL Query Optimization (optional)
# Customize pagination limits for repository data fetching
# Defaults to 100 for all if not specified
# Maximum: 100 (GitHub GraphQL API limit)
graphql:
query-limits:
collaborators: 100 # Max collaborators to fetch per query
contributors: 100 # Max contributors (mentionableUsers) to fetch
issues: 100 # Max open issues to fetch
pull-requests: 100 # Max open pull requests to fetch
labels: 100 # NEW: Max labels to fetch per PR/issue
reviews: 100 # NEW: Max reviews to fetch per PR
commits: 100 # NEW: Max commits to fetch per PR
comments: 100 # NEW: Max comments to fetch per PR
assignees: 100 # NEW: Max assignees to fetch per PR
files: 100 # NEW: Max changed files to fetch per PR
associated-pull-requests: 100 # NEW: Max associated PRs to fetch per commit

# Global PR size label configuration (optional)
# Define custom categories based on total lines changed (additions + deletions)
# threshold: positive integer representing minimum lines changed for this category
Expand Down
Loading