Skip to content

feat: support user-defined custom check runs #961

Merged
myakove merged 39 commits intomainfrom
add-plugin-option
Jan 19, 2026
Merged

feat: support user-defined custom check runs #961
myakove merged 39 commits intomainfrom
add-plugin-option

Conversation

@rnetser
Copy link
Copy Markdown
Collaborator

@rnetser rnetser commented Dec 30, 2025

The feature allows users to define custom checks in their YAML config that:

  • Execute uv tool run commands securely
  • Support configurable triggers (opened, synchronize, reopened, etc.)
  • Include secret redaction in logs
  • Integrate with GitHub Check Runs API

Summary by CodeRabbit

  • New Features

    • Per-repo configurable custom check runs shown in welcome/retest lists, runnable via /retest, and reported as PR status checks; built-in checks protected.
  • Refactor

    • Unified, name-based check-run lifecycle and standardized status payloads; custom checks integrated into CI/retest flows.
  • Tests

    • Extensive coverage for schema, validation, execution, env handling, retests, and edge cases.
  • Documentation

    • Added Security guidance for custom check runs in README.
  • Style

    • Exposed duration-formatting helper for build summaries.

✏️ Tip: You can customize this high-level summary in your review settings.

Address code review findings to improve stability and safety:

- Replace unsafe dictionary access with .get() method at 5 locations
  to prevent KeyError crashes when 'name' field is missing
- Add empty-string validation for secret redaction to prevent
  whitespace in redaction list
- Add COMPLETED_STR constant for check run status
- Implement comprehensive command security validation
- Add dedicated test coverage for command security and custom checks

This ensures custom check runs fail gracefully with proper logging
rather than crashing on malformed configuration.
@rnetser
Copy link
Copy Markdown
Collaborator Author

rnetser commented Dec 30, 2025

/wip

@myakove-bot
Copy link
Copy Markdown
Collaborator

Report bugs in Issues

Welcome! 🎉

This pull request will be automatically processed with the following features:

🔄 Automatic Actions

  • Reviewer Assignment: Reviewers are automatically assigned based on the OWNERS file in the repository root
  • Size Labeling: PR size labels (XS, S, M, L, XL, XXL) are automatically applied based on changes
  • Issue Creation: A tracking issue is created for this PR and will be closed when the PR is merged or closed
  • Pre-commit Checks: pre-commit runs automatically if .pre-commit-config.yaml exists
  • Branch Labeling: Branch-specific labels are applied to track the target branch
  • Auto-verification: Auto-verified users have their PRs automatically marked as verified

📋 Available Commands

PR Status Management

  • /wip - Mark PR as work in progress (adds WIP: prefix to title)
  • /wip cancel - Remove work in progress status
  • /hold - Block PR merging (approvers only)
  • /hold cancel - Unblock PR merging
  • /verified - Mark PR as verified
  • /verified cancel - Remove verification status
  • /reprocess - Trigger complete PR workflow reprocessing (useful if webhook failed or configuration changed)

Review & Approval

  • /lgtm - Approve changes (looks good to me)
  • /approve - Approve PR (approvers only)
  • /automerge - Enable automatic merging when all requirements are met (maintainers and approvers only)
  • /assign-reviewers - Assign reviewers based on OWNERS file
  • /assign-reviewer @username - Assign specific reviewer
  • /check-can-merge - Check if PR meets merge requirements

Testing & Validation

  • /retest tox - Run Python test suite with tox
  • /retest build-container - Rebuild and test container image
  • /retest python-module-install - Test Python package installation
  • /retest pre-commit - Run pre-commit hooks and checks
  • /retest conventional-title - Validate commit message format
  • /retest all - Run all available tests

Container Operations

  • /build-and-push-container - Build and push container image (tagged with PR number)
    • Supports additional build arguments: /build-and-push-container --build-arg KEY=value

Cherry-pick Operations

  • /cherry-pick <branch> - Schedule cherry-pick to target branch when PR is merged
    • Multiple branches: /cherry-pick branch1 branch2 branch3

Label Management

  • /<label-name> - Add a label to the PR
  • /<label-name> cancel - Remove a label from the PR

✅ Merge Requirements

This PR will be automatically approved when the following conditions are met:

  1. Approval: /approve from at least one approver
  2. LGTM Count: Minimum 1 /lgtm from reviewers
  3. Status Checks: All required status checks must pass
  4. No Blockers: No WIP, hold, or conflict labels
  5. Verified: PR must be marked as verified (if verification is enabled)

📊 Review Process

Approvers and Reviewers

Approvers:

  • myakove
  • rnetser

Reviewers:

  • myakove
  • rnetser
Available Labels
  • hold
  • verified
  • wip
  • lgtm
  • approve
  • automerge

💡 Tips

  • WIP Status: Use /wip when your PR is not ready for review
  • Verification: The verified label is automatically removed on each new commit
  • Cherry-picking: Cherry-pick labels are processed when the PR is merged
  • Container Builds: Container images are automatically tagged with the PR number
  • Permission Levels: Some commands require approver permissions
  • Auto-verified Users: Certain users have automatic verification and merge privileges

For more information, please refer to the project documentation or contact the maintainers.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 30, 2025

Walkthrough

Adds repository-configurable custom check runs (name, command, optional mandatory) and validation; exposes generic named check-run APIs; centralizes runner logic to execute built-in and custom checks (including /retest orchestration); updates schema, tests, and docs.

Changes

Cohort / File(s) Summary
Configuration Schema
webhook_server/config/schema.yaml
Add pr-size-thresholds.custom-check-runs: list of objects requiring name and command, optional mandatory (default true); items forbid additional properties.
Github webhook / config loading
webhook_server/libs/github_api.py
Load and validate custom-check-runs into GithubWebhook.custom_check_runs; adds _validate_custom_check_runs (name safety, uniqueness, command parsing, executable existence via shutil.which), and appends custom names to supported retest targets.
Check run status API
webhook_server/libs/handlers/check_run_handler.py
Replace many per-check helpers with generic APIs: set_check_queued(name, output=None), set_check_in_progress(name), set_check_success(name, output=None), set_check_failure(name, output=None); add CheckRunOutput TypedDict; include mandatory custom checks in required checks; remove specialized setters/constants.
Retest orchestration & execution
webhook_server/libs/handlers/runner_handler.py
Add CheckConfig dataclass and unified run_check, run_custom_check, and run_retests to handle checkout, placeholder/env handling, timeouts, command execution, and reporting via CheckRunHandler.
PR event integration
webhook_server/libs/handlers/pull_request_handler.py
Queue and list configured custom checks at setup using generic check APIs; run custom checks via runner_handler.run_custom_check; include mandatory flag in welcome/retest messaging.
Issue comment retest flow
webhook_server/libs/handlers/issue_comment_handler.py
Replace per-test dispatch and manual gather with runner_handler.run_retests(...); use generic set_check_* APIs; preserve unsupported/error handling.
Constants & settings
webhook_server/utils/constants.py, webhook_server/utils/github_repository_settings.py
Add BUILTIN_CHECK_NAMES: frozenset[str]; extend REACTIONS; propagate BUILTIN_CHECK_NAMES into settings APIs and change param type to frozenset[str].
Tests & fixtures
webhook_server/tests/...
Add tests/test_custom_check_runs.py; update many tests/fixtures to add mock_webhook.custom_check_runs = []; switch mocks to generic set_check_*; add tests for collisions, duplicates, env propagation, cwd/timeout, and /retest behavior.
Utilities/tests
webhook_server/tests/test_context.py
Export and test _format_duration for build summaries.
Docs
README.md
Add Security section describing risks and recommendations for repository-defined custom check commands (duplicate insertion observed).

Sequence Diagram(s)

sequenceDiagram
    participant GH as GitHub API
    participant PR as Pull Request Event
    participant PRH as PullRequestHandler
    participant CH as CheckRunHandler
    participant RH as RunnerHandler
    participant WT as Worktree
    participant EX as Executor

    GH->>PR: emit pull_request event
    PR->>PRH: process event (setup)
    PRH->>CH: set_check_queued(name="custom-check")
    CH->>GH: create check run (queued)

    PRH->>RH: run_custom_check(pull_request, CheckConfig)
    RH->>CH: set_check_in_progress(name="custom-check")
    CH->>GH: update check run (in_progress)

    RH->>WT: prepare / checkout PR worktree
    RH->>EX: execute command (cwd=worktree, env=merged)

    alt command succeeds
        RH->>CH: set_check_success(name="custom-check", output)
        CH->>GH: update check run (success)
    else fails / not found / timeout
        RH->>CH: set_check_failure(name="custom-check", output)
        CH->>GH: update check run (failure)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

commented-rnetser, commented-coderabbitai[bot]

Suggested reviewers

  • myakove

Review notes (severity-guided)

CRITICAL (blocking): Ensure custom check names cannot collide with built-in check names. Why: collisions can silently override required checks. Verify BUILTIN_CHECK_NAMES is complete and enforced in _validate_custom_check_runs.

HIGH (types/defensive): Confirm _validate_custom_check_runs strips environment assignments before calling shutil.which() and that PATH propagation intended for runtime is safe. Why: incorrect detection may accept non-executable commands or reject valid ones.

MEDIUM (style/consistency): Verify removal of many specific setters from public surface won't break external consumers; ensure docs and any integrations are updated.

LOW (suggestion): Document env semantics, use_cwd behavior, and failure semantics (missing executable vs runtime failure) in README and schema examples.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: support user-defined custom check runs' accurately and concisely summarizes the main feature addition in this PR.
Docstring Coverage ✅ Passed Docstring coverage is 83.63% which is sufficient. The required threshold is 80.00%.

✏️ 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
  • Commit unit tests in branch add-plugin-option

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.

@rnetser rnetser marked this pull request as draft December 30, 2025 11:28
@myakove-bot myakove-bot changed the title feat: support user-defined custom check runs WIP: feat: support user-defined custom check runs Dec 30, 2025
Copy link
Copy Markdown

@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: 7

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20aabd2 and c2a367f.

📒 Files selected for processing (14)
  • webhook_server/config/schema.yaml
  • webhook_server/libs/github_api.py
  • webhook_server/libs/handlers/check_run_handler.py
  • webhook_server/libs/handlers/issue_comment_handler.py
  • webhook_server/libs/handlers/pull_request_handler.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/tests/test_check_run_handler.py
  • webhook_server/tests/test_command_security.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_issue_comment_handler.py
  • webhook_server/tests/test_pull_request_handler.py
  • webhook_server/utils/command_security.py
  • webhook_server/utils/constants.py
🧰 Additional context used
📓 Path-based instructions (7)
webhook_server/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

webhook_server/**/*.py: Never use defensive checks (hasattr, if object:, if value is not None) on required parameters passed to __init__() - required parameters are always provided in this self-contained server application
Defensive checks ARE acceptable in destructors (__del__) because they may be called during failed initialization
Defensive checks ARE acceptable for lazy initialization where attributes explicitly start as None - check is legitimate for uninitialized attributes
Never use defensive checks on known library versions controlled in pyproject.toml - e.g., PyGithub >=2.4.0 methods are guaranteed to exist
Never use defensive checks on webhook payload fields guaranteed by GitHub webhook specification - e.g., user.node_id, user.type, sender always exist in webhook payloads
Never use hasattr() for type discrimination - use isinstance() instead for type checking
Never return fake default values ('', 0, False, None objects, []) to hide missing data - raise exceptions with clear error messages instead to enable fail-fast debugging
ALL PyGithub method calls MUST be wrapped with asyncio.to_thread() to avoid blocking the FastAPI event loop - including .get_*(), .create_*(), .edit(), .add_to_*(), .remove_from_*()
ALL PyGithub property accesses that may trigger API calls MUST be wrapped with asyncio.to_thread() - properties like .draft, .mergeable, .state, .committer, .permissions, .labels, .assignees are NOT safe to access directly
PyGithub PaginatedList iteration MUST be wrapped in asyncio.to_thread() with list conversion - never iterate directly to avoid blocking the event loop
ALL imports must be at the top of files - no imports in the middle of functions or try/except blocks except for TYPE_CHECKING conditional imports
ALL functions must have complete type hints compatible with mypy strict mode - parameter types, return types, and complex object types must be explicitly annotated
Code coverage of 90% or higher is ...

Files:

  • webhook_server/utils/constants.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/issue_comment_handler.py
  • webhook_server/libs/handlers/pull_request_handler.py
  • webhook_server/tests/test_issue_comment_handler.py
  • webhook_server/tests/test_check_run_handler.py
  • webhook_server/tests/test_github_api.py
  • webhook_server/utils/command_security.py
  • webhook_server/tests/test_command_security.py
  • webhook_server/tests/test_pull_request_handler.py
  • webhook_server/libs/handlers/check_run_handler.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
webhook_server/**/*.{py,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Defensive checks ARE acceptable for optional parameters explicitly typed as Type | None - check is legitimate for parameters allowing None

Files:

  • webhook_server/utils/constants.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/issue_comment_handler.py
  • webhook_server/libs/handlers/pull_request_handler.py
  • webhook_server/tests/test_issue_comment_handler.py
  • webhook_server/tests/test_check_run_handler.py
  • webhook_server/tests/test_github_api.py
  • webhook_server/utils/command_security.py
  • webhook_server/tests/test_command_security.py
  • webhook_server/tests/test_pull_request_handler.py
  • webhook_server/libs/handlers/check_run_handler.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
webhook_server/config/schema.yaml

📄 CodeRabbit inference engine (CLAUDE.md)

Configuration schema must be defined in webhook_server/config/schema.yaml with validation logic in webhook_server/libs/config.py

Files:

  • webhook_server/config/schema.yaml
webhook_server/tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Test files MUST use TEST_GITHUB_TOKEN = 'ghp_test1234...' with # pragma: allowlist secret comment to avoid security warnings in test tokens

Files:

  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/test_issue_comment_handler.py
  • webhook_server/tests/test_check_run_handler.py
  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_command_security.py
  • webhook_server/tests/test_pull_request_handler.py
webhook_server/libs/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

webhook_server/libs/**/*.py: Internal methods in webhook_server/libs/ can change freely without backward compatibility requirements - return types, method signatures, and implementations may be modified without deprecation warnings
Eliminate unnecessary defensive checks for return type changes and method signature modifications in internal APIs - fail-fast principle is preferred over compatibility

Files:

  • webhook_server/libs/handlers/issue_comment_handler.py
  • webhook_server/libs/handlers/pull_request_handler.py
  • webhook_server/libs/handlers/check_run_handler.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
webhook_server/libs/handlers/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

webhook_server/libs/handlers/**/*.py: Architecture guarantee: repository_data is ALWAYS set before handlers instantiate in GithubWebhook.process() with fail-fast exceptions - no defensive checks needed
Use await asyncio.gather(*tasks, return_exceptions=True) for concurrent non-blocking PyGithub operations to enable parallel processing of multiple GitHub API calls
Use structured logging with contextual parameters via get_logger_with_params() including repository, hook_id, and component-specific identifiers for webhook correlation
Configuration access via Config(repository='org/repo-name') - repository parameter is required for per-repository overrides via .github-webhook-server.yaml
Handler pattern: implement __init__(self, github_webhook: GithubWebhook, ...) and process_event(event_data: dict) -> None for all GitHub event handlers
Use self.github_webhook.unified_api for all GitHub API operations in handlers - never access GitHub API directly

Files:

  • webhook_server/libs/handlers/issue_comment_handler.py
  • webhook_server/libs/handlers/pull_request_handler.py
  • webhook_server/libs/handlers/check_run_handler.py
  • webhook_server/libs/handlers/runner_handler.py
webhook_server/libs/handlers/*check_run*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Repository cloning for check_run webhook events MUST be optimized by skipping clone when action != 'completed' or when check name is 'can-be-merged' with non-success conclusion

Files:

  • webhook_server/libs/handlers/check_run_handler.py
🧠 Learnings (19)
📚 Learning: 2025-12-29T13:09:56.231Z
Learnt from: rnetser
Repo: myk-org/github-webhook-server PR: 959
File: webhook_server/libs/handlers/pull_request_handler.py:887-887
Timestamp: 2025-12-29T13:09:56.231Z
Learning: In the myk-org/github-webhook-server repository, logging statements in the webhook_server codebase should use f-strings for interpolation. Do not suggest changing to lazy formatting with % (e.g., logger.debug('msg %s', var)) since that is an established style choice. When adding new log statements, prefer f"...{var}..." for readability and performance, and avoid string concatenation or format-style logging unless the project explicitly adopts a different convention.

Applied to files:

  • webhook_server/utils/constants.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/issue_comment_handler.py
  • webhook_server/libs/handlers/pull_request_handler.py
  • webhook_server/tests/test_issue_comment_handler.py
  • webhook_server/tests/test_check_run_handler.py
  • webhook_server/tests/test_github_api.py
  • webhook_server/utils/command_security.py
  • webhook_server/tests/test_command_security.py
  • webhook_server/tests/test_pull_request_handler.py
  • webhook_server/libs/handlers/check_run_handler.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Applies to webhook_server/config/schema.yaml : Configuration schema must be defined in `webhook_server/config/schema.yaml` with validation logic in `webhook_server/libs/config.py`

Applied to files:

  • webhook_server/config/schema.yaml
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Applies to {config.yaml,.github-webhook-server.yaml} : Maintain backward compatibility ONLY for user-facing configuration files (`config.yaml`, `.github-webhook-server.yaml`) - configuration schema changes must support old formats or provide migration paths

Applied to files:

  • webhook_server/config/schema.yaml
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Applies to webhook_server/libs/handlers/*check_run*.py : Repository cloning for check_run webhook events MUST be optimized by skipping clone when action != 'completed' or when check name is 'can-be-merged' with non-success conclusion

Applied to files:

  • webhook_server/config/schema.yaml
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/pull_request_handler.py
  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/handlers/check_run_handler.py
  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Applies to webhook_server/tests/**/*.py : Test files MUST use `TEST_GITHUB_TOKEN = 'ghp_test1234...'` with `# pragma: allowlist secret` comment to avoid security warnings in test tokens

Applied to files:

  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/test_issue_comment_handler.py
  • webhook_server/tests/test_check_run_handler.py
  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_command_security.py
  • webhook_server/tests/test_pull_request_handler.py
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Applies to webhook_server/**/*.py : Code coverage of 90% or higher is MANDATORY - measured via `pytest --cov=webhook_server` and enforced in CI

Applied to files:

  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/test_command_security.py
📚 Learning: 2024-10-15T10:37:45.791Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 598
File: webhook_server_container/libs/github_api.py:1860-1874
Timestamp: 2024-10-15T10:37:45.791Z
Learning: In the `process_retest_command` method in `webhook_server_container/libs/github_api.py`, `_target_tests` is defined before use.

Applied to files:

  • webhook_server/libs/handlers/issue_comment_handler.py
  • webhook_server/tests/test_check_run_handler.py
📚 Learning: 2025-05-13T12:06:27.297Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 778
File: webhook_server/libs/pull_request_handler.py:327-330
Timestamp: 2025-05-13T12:06:27.297Z
Learning: In the GitHub webhook server, synchronous GitHub API calls (like create_issue_comment, add_to_assignees, etc.) in async methods should be awaited using asyncio.to_thread or loop.run_in_executor to prevent blocking the event loop.

Applied to files:

  • webhook_server/libs/handlers/issue_comment_handler.py
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Applies to webhook_server/libs/handlers/**/*.py : Use `await asyncio.gather(*tasks, return_exceptions=True)` for concurrent non-blocking PyGithub operations to enable parallel processing of multiple GitHub API calls

Applied to files:

  • webhook_server/libs/handlers/pull_request_handler.py
📚 Learning: 2024-11-26T14:30:22.906Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 631
File: webhook_server_container/tests/test_github_api.py:321-327
Timestamp: 2024-11-26T14:30:22.906Z
Learning: In `webhook_server_container/tests/test_github_api.py`, when using `pytest.mark.parametrize` with indirect parameters, the `pytest.param` function may require nested lists to match the expected input structure.

Applied to files:

  • webhook_server/tests/test_check_run_handler.py
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Applies to webhook_server/**/*.py : Never use defensive checks on known library versions controlled in `pyproject.toml` - e.g., PyGithub >=2.4.0 methods are guaranteed to exist

Applied to files:

  • webhook_server/tests/test_check_run_handler.py
  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Applies to webhook_server/**/*.py : Defensive checks ARE acceptable for lazy initialization where attributes explicitly start as `None` - check is legitimate for uninitialized attributes

Applied to files:

  • webhook_server/tests/test_check_run_handler.py
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Applies to webhook_server/libs/handlers/**/*.py : Architecture guarantee: `repository_data` is ALWAYS set before handlers instantiate in `GithubWebhook.process()` with fail-fast exceptions - no defensive checks needed

Applied to files:

  • webhook_server/tests/test_check_run_handler.py
  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Applies to webhook_server/libs/**/*.py : Eliminate unnecessary defensive checks for return type changes and method signature modifications in internal APIs - fail-fast principle is preferred over compatibility

Applied to files:

  • webhook_server/utils/command_security.py
  • webhook_server/tests/test_command_security.py
📚 Learning: 2025-10-28T13:04:00.466Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 878
File: webhook_server/libs/handlers/runner_handler.py:491-571
Timestamp: 2025-10-28T13:04:00.466Z
Learning: In webhook_server/libs/handlers/runner_handler.py, the run_build_container method is designed with the pattern that push=True is always called with set_check=False in production code, so no check-run status needs to be finalized after push operations.

Applied to files:

  • webhook_server/libs/handlers/check_run_handler.py
  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Applies to webhook_server/**/*.py : Never use defensive checks (hasattr, if object:, if value is not None) on required parameters passed to `__init__()` - required parameters are always provided in this self-contained server application

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Maintain backward compatibility for webhook payload handling - must follow GitHub webhook specification to ensure compatibility with GitHub API changes

Applied to files:

  • webhook_server/libs/github_api.py
📚 Learning: 2024-10-29T08:09:57.157Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 612
File: webhook_server_container/libs/github_api.py:2089-2100
Timestamp: 2024-10-29T08:09:57.157Z
Learning: In `webhook_server_container/libs/github_api.py`, when the function `_keep_approved_by_approvers_after_rebase` is called, existing approval labels have already been cleared after pushing new changes, so there's no need to check for existing approvals within this function.

Applied to files:

  • webhook_server/libs/github_api.py
📚 Learning: 2025-11-25T12:39:43.079Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T12:39:43.079Z
Learning: Applies to webhook_server/libs/handlers/**/*.py : Configuration access via `Config(repository='org/repo-name')` - repository parameter is required for per-repository overrides via `.github-webhook-server.yaml`

Applied to files:

  • webhook_server/libs/github_api.py
🧬 Code graph analysis (7)
webhook_server/tests/test_custom_check_runs.py (2)
webhook_server/libs/handlers/check_run_handler.py (6)
  • set_custom_check_queued (247-249)
  • set_custom_check_in_progress (251-253)
  • set_custom_check_success (255-262)
  • set_custom_check_failure (264-271)
  • all_required_status_checks (488-523)
  • get_check_run_text (328-381)
webhook_server/libs/handlers/runner_handler.py (1)
  • run_custom_check (626-705)
webhook_server/libs/handlers/pull_request_handler.py (2)
webhook_server/libs/handlers/check_run_handler.py (1)
  • set_custom_check_queued (247-249)
webhook_server/libs/handlers/runner_handler.py (1)
  • run_custom_check (626-705)
webhook_server/tests/test_github_api.py (4)
webhook_server/tests/test_helpers.py (3)
  • get_value_side_effect (176-183)
  • get_value_side_effect (199-206)
  • get_value_side_effect (222-229)
webhook_server/tests/test_webhook.py (1)
  • mock_config (319-329)
webhook_server/tests/test_github_repository_and_webhook_settings.py (1)
  • mock_config (66-70)
webhook_server/libs/config.py (1)
  • get_value (99-120)
webhook_server/tests/test_command_security.py (1)
webhook_server/utils/command_security.py (2)
  • CommandSecurityResult (13-17)
  • validate_command_security (72-113)
webhook_server/libs/handlers/check_run_handler.py (3)
webhook_server/web/static/js/log_viewer.js (1)
  • status (12-12)
webhook_server/tests/conftest.py (1)
  • github_webhook (111-131)
webhook_server/tests/test_github_api.py (1)
  • logger (75-76)
webhook_server/libs/handlers/runner_handler.py (3)
webhook_server/utils/command_security.py (1)
  • validate_command_security (72-113)
webhook_server/libs/handlers/check_run_handler.py (3)
  • set_custom_check_failure (264-271)
  • set_custom_check_in_progress (251-253)
  • get_check_run_text (328-381)
webhook_server/utils/helpers.py (2)
  • format_task_fields (135-154)
  • run_command (288-439)
webhook_server/libs/github_api.py (1)
webhook_server/libs/config.py (1)
  • get_value (99-120)
🪛 Ruff (0.14.10)
webhook_server/tests/test_custom_check_runs.py

271-271: Probable insecure usage of temporary file or directory: "/tmp/test-repo"

(S108)


306-306: Probable insecure usage of temporary file or directory: "/tmp/worktree"

(S108)


338-338: Probable insecure usage of temporary file or directory: "/tmp/worktree"

(S108)


387-387: Probable insecure usage of temporary file or directory: "/tmp/worktree"

(S108)


416-416: Probable insecure usage of temporary file or directory: "/tmp/test-worktree"

(S108)


432-432: Probable insecure usage of temporary file or directory: "/tmp/test-worktree"

(S108)


467-467: Probable insecure usage of temporary file or directory: "/tmp/worktree"

(S108)


534-534: Probable insecure usage of temporary file or directory: "/tmp/test-repo"

(S108)


572-572: Unused method argument: mock_pull_request

(ARG002)


595-595: Unused method argument: mock_pull_request

(ARG002)


620-620: Unused method argument: mock_pull_request

(ARG002)


654-654: Probable insecure usage of temporary file or directory: "/tmp/worktree"

(S108)


728-728: Probable insecure usage of temporary file or directory: "/tmp/worktree"

(S108)


764-764: Probable insecure usage of temporary file or directory: "/tmp/test-repo"

(S108)


812-812: Probable insecure usage of temporary file or directory: "/tmp/worktree"

(S108)


846-846: Probable insecure usage of temporary file or directory: "/tmp/worktree"

(S108)

webhook_server/libs/handlers/pull_request_handler.py

750-750: Logging statement uses f-string

(G004)

webhook_server/tests/test_github_api.py

1405-1405: Dynamically typed expressions (typing.Any) are disallowed in *_args

(ANN401)


1405-1405: Dynamically typed expressions (typing.Any) are disallowed in **_kwargs

(ANN401)


1405-1405: Dynamically typed expressions (typing.Any) are disallowed in get_value_side_effect

(ANN401)

webhook_server/libs/handlers/check_run_handler.py

516-516: Logging statement uses f-string

(G004)

webhook_server/libs/handlers/runner_handler.py

634-634: Logging statement uses f-string

(G004)


643-643: Logging statement uses f-string

(G004)


838-838: Logging statement uses f-string

(G004)


854-854: Logging statement uses f-string

(G004)


868-868: Logging statement uses f-string

(G004)


875-875: Logging statement uses f-string

(G004)

webhook_server/libs/github_api.py

827-827: Logging statement uses f-string

(G004)

🔇 Additional comments (29)
webhook_server/libs/handlers/issue_comment_handler.py (2)

5-5: Use of Coroutine type alias is correct and consistent (Severity: LOW)

The new from collections.abc import Coroutine import matches the tasks: list[Coroutine[..., Any] | Task[Any]] annotation below and keeps typing explicit without affecting runtime behavior. No issues here.


449-454: Delegating retests to RunnerHandler.run_retests is a good DRY/consolidation (Severity: LOW)

Switching from per-test handling to:

if _supported_retests:
    await self.runner_handler.run_retests(
        supported_retests=_supported_retests,
        pull_request=pull_request,
    )

centralizes all retest execution (including future custom checks) in RunnerHandler, reducing duplicated logic and keeping IssueCommentHandler.process_retest_command focused on parsing/validation. Behavior for unsupported tests and automerge labeling remains unchanged in this method.

webhook_server/tests/test_check_run_handler.py (1)

49-50: Fixture now mirrors GithubWebhook.custom_check_runs surface (Severity: LOW)

Initializing mock_webhook.custom_check_runs = [] keeps the test fixture aligned with the production webhook object and prevents attribute errors when custom check logic inspects this list. This is the right place to wire in the new feature without altering test behavior.

webhook_server/tests/test_issue_comment_handler.py (1)

43-44: Aligns mock_github_webhook with new custom_check_runs API (Severity: LOW)

Adding mock_webhook.custom_check_runs = [] ensures IssueCommentHandler (and downstream runners) can safely access the new configuration-driven custom check runs field during tests, avoiding brittle hasattr checks in production.

webhook_server/tests/test_pull_request_handler.py (1)

82-83: Pull request tests updated for custom_check_runs presence (Severity: LOW)

Defining mock_webhook.custom_check_runs = [] in the PR handler fixture correctly mirrors the real webhook object after introducing custom check runs, so handler logic can treat this attribute as always present without extra guards.

webhook_server/utils/constants.py (1)

10-10: COMPLETED_STR status constant matches existing pattern (Severity: LOW)

Introducing COMPLETED_STR: str = "completed" is consistent with the other status constants and will reduce magic strings in check-run/custom-check flows. No functional risk here, and it improves readability and reuse.

webhook_server/tests/test_github_api.py (1)

88-106: Custom-check-runs test config defaults are consistent (severity: LOW).

Returning [] for "custom-check-runs" in these get_value_side_effect helpers keeps GithubWebhook.custom_check_runs reliably iterable in tests and mirrors the production default behavior, so the new logic should integrate cleanly with the custom-check-runs feature. No changes needed here.

Also applies to: 638-669

webhook_server/config/schema.yaml (1)

321-369: Custom-check-runs schema matches the runtime model (severity: LOW).

The custom-check-runs definition (required name/command, uv tool run --from pattern, bounded timeout, required flag, constrained triggers, and constrained secrets) aligns with how GithubWebhook.custom_check_runs, PullRequestHandler, and RunnerHandler.run_custom_check consume this config. The secrets pattern and required list also back up the secret-redaction behavior. Looks good.

webhook_server/libs/github_api.py (1)

646-688: custom_check_runs loading and retest list integration look correct (severity: LOW).

The _repo_data_from_config change ensures self.custom_check_runs is always a list (defaulting to []), and _current_pull_request_supported_retest safely adds only entries with a defined name, logging a warning otherwise. This matches the new schema and how RunnerHandler.run_retests and PullRequestHandler consume these names. No issues spotted.

Also applies to: 805-831

webhook_server/libs/handlers/runner_handler.py (1)

626-706: Custom check execution path is secure and well-scoped (severity: LOW).

run_custom_check:

  • Validates presence of name and fails fast with a clear log if missing.
  • Enforces command safety up front via validate_command_security, turning failures into a dedicated failed check with explanatory output.
  • Collects secrets from env vars and passes only non-empty values into run_command.redact_secrets, matching the schema’s “redact from logs” contract.
  • Runs inside _checkout_worktree and consistently updates check-run state via the new set_custom_check_* helpers.

This aligns with the security and logging goals for custom checks.

webhook_server/libs/handlers/check_run_handler.py (1)

16-26: Custom check status helpers and required-check wiring look solid (severity: LOW).

  • The new set_custom_check_* helpers correctly wrap set_check_run_status using COMPLETED_STR plus SUCCESS_STR/FAILURE_STR, matching GitHub’s check-run lifecycle.
  • all_required_status_checks now folds in custom_check_runs flagged as required, and logs a warning when name is missing instead of raising, which is consistent with other components and gives clean diagnostics for bad config.

This integrates custom checks cleanly into the existing required-status and merge-eligibility logic.

Also applies to: 247-272, 511-519

webhook_server/utils/command_security.py (3)

1-17: LGTM! Clean module structure and NamedTuple definition.

The docstring clearly states the purpose, imports are minimal and at the top, and CommandSecurityResult is a well-typed NamedTuple providing clear semantics for validation results.


68-69: LGTM! Reasonable maximum command length.

4096 characters is a sensible limit that prevents buffer-based attacks while allowing legitimate long commands with multiple arguments.


72-113: LOW: Empty and whitespace-only commands pass validation—verify this is intentional.

The function returns is_safe=True for empty strings and whitespace-only commands. While not a security vulnerability (executing an empty command is harmless), downstream code should handle this edge case. The current behavior is documented by tests, so this is just a note for awareness.

The validation flow is well-structured: length → dangerous patterns → sensitive paths → null bytes → non-printable chars. The fail-fast approach with early returns is clean and efficient.

webhook_server/tests/test_custom_check_runs.py (6)

1-35: LGTM! Comprehensive test module with clear documentation.

The module docstring clearly explains what's being tested (schema, handlers, runner, integration, retest). Imports are organized correctly at the top, and constants are imported from the appropriate module.


37-120: LOW: Schema validation tests document expectations rather than validate schema.

These tests verify the structure of config dictionaries matches expectations, but don't actually run the YAML schema validator. This is acceptable as documentation tests, but consider adding a note that actual schema validation happens elsewhere.

The tests effectively document:

  • Required fields (name, command)
  • Optional fields with defaults (timeout=600, required=true)
  • Valid trigger values
  • Secrets configuration format

122-258: LGTM! Handler method tests are thorough and well-structured.

Tests correctly:

  • Mock set_check_run_status to verify delegation
  • Test all four custom check methods (queued, in_progress, success, failure)
  • Verify correct status constants are passed
  • Test all_required_status_checks includes/excludes custom checks based on required flag
  • Properly reset cache before recalculation tests

260-518: LGTM! Runner handler tests cover critical execution paths.

Excellent coverage of:

  • Success and failure scenarios
  • Checkout failure handling
  • Default timeout application
  • Worktree directory usage
  • Secrets redaction (line 447 correctly uses # pragma: allowlist secret)
  • Security validation rejection

The async context manager mocking pattern is correct and reusable.


826-859: LGTM! Edge case test for special characters correctly validates security blocking.

The test verifies that commands containing $variables are blocked by security validation, with the check failing before command execution. This confirms the defense-in-depth approach works as intended.


793-824: HIGH: Test contradicts production behavior—run_command catches TimeoutError and returns (False, "", message).

Lines 823-824 mock run_command to raise asyncio.TimeoutError, but the actual run_command implementation (webhook_server/utils/helpers.py, lines 355-364) catches TimeoutError and returns (False, "", f"Command timed out after {timeout}s") instead of propagating it.

When run_custom_check receives success=False from run_command, it properly sets failure status via set_custom_check_failure() (line 704). The test mock does not reflect this behavior—it should mock run_command to return (False, "", "Command timed out after 30s") instead of raising an exception.

Likely an incorrect or invalid review comment.

webhook_server/tests/test_command_security.py (9)

1-16: LGTM! Clean test module setup.

Imports are organized, the docstring clearly states purpose, and imports from command_security module are correct.


18-69: LGTM! Good coverage of valid command patterns.

Both uv tool run variants and simple commands are tested. The parametrized approach ensures maintainability.


71-141: LGTM! Thorough shell injection prevention tests.

Excellent coverage of:

  • Semicolon, pipe, logical operators
  • Command substitution ($() and backticks)
  • Variable expansion (${} and $VAR)
  • Redirections
  • Background execution
  • Newline escapes
  • Dangerous builtins (eval, exec, source)

The comments explaining pattern matching order (e.g., line 85-86) are helpful for understanding test expectations.


144-195: LGTM! Dangerous command blocking tests are comprehensive.

Good coverage of shell spawning, network tools, privilege escalation, file permission changes, and destructive operations. Case-insensitivity tests ensure bypass attempts are caught.

Line 156's comment correctly documents that wget malicious.sh matches \bsh\b in the .sh extension—this is intentional behavior since the wget pattern also triggers.


197-261: LGTM! Sensitive path access tests cover key vectors.

System directories, process/system info, logs, boot partition, SSH keys, path traversal, environment files, config files, credentials, and private keys are all tested. Case-insensitivity tests included.


338-366: LOW: Consider adding a comment about empty command policy.

Lines 341-351 test that empty and whitespace-only commands pass validation. While this matches the implementation, adding a brief comment explaining WHY this is acceptable behavior would help future maintainers:

  • Empty commands are harmless to execute
  • The caller (runner_handler) validates command presence separately

385-400: LGTM! Public API shape verification.

Testing both named attribute access and index-based access on CommandSecurityResult ensures the NamedTuple contract is maintained.


402-454: LGTM! Real-world examples provide practical validation.

The mix of valid and invalid commands with inline logic (lines 431-434) to determine expected outcome is a clean approach. Disguised malicious commands test defense-in-depth against obfuscation attempts.


456-484: LGTM! Performance and consistency tests add confidence.

The 300 commands in <1 second threshold is reasonable for regex-based validation. Consistency test verifies deterministic behavior.

Comment thread webhook_server/libs/handlers/pull_request_handler.py Outdated
Comment thread webhook_server/tests/test_command_security.py Outdated
Comment thread webhook_server/tests/test_custom_check_runs.py
Comment thread webhook_server/tests/test_github_api.py Outdated
Comment thread webhook_server/utils/command_security.py Outdated
Comment thread webhook_server/utils/command_security.py Outdated
Remove complex security validation layer and adopt a trust-based approach:

- Delete command_security.py and related security validation logic
- Simplify schema to only name, command, env fields
- Add shutil.which() check to gracefully skip unavailable commands
- Add set_custom_check_skipped() for handling missing commands
- Update tests to reflect simplified validation model

This change assumes users will configure safe commands and gracefully
handles cases where commands are not found instead of blocking them.
Simplify env configuration by specifying only variable names instead of
key-value pairs. Values are read from server environment at runtime.

Changes:
- Schema: env changed from object to array of strings
- Handler: reads env var values from os.environ
- Tests: updated for new env list format
Added example showing environment variable reference in custom-check-runs
commands to improve documentation clarity.
Remove special handling for custom checks - they now fail naturally like
built-in checks (tox, pre-commit, etc.) when commands are not found.

Changes:
- runner_handler.py: Removed skip logic for missing commands
- check_run_handler.py: Removed set_custom_check_skipped method
- schema.yaml: Simplified description to reflect new behavior
- test_custom_check_runs.py: Removed skip/trigger/timeout tests
Allows env entries in custom check run configuration to use two formats:
- Variable name only (VAR) - reads from server environment
- Variable with explicit value (VAR=value) - uses provided value

This enables mixed usage like:
  env:
    - PYTHONPATH
    - DEBUG=true
Custom checks now behave exactly like built-in checks (tox, pre-commit):
- Run on all PR events (no trigger filtering)
- Always required for merge (no required field)
- Validate name, command, and executable existence at config load time
- Remove duplicate validation from 5 locations
- Remove configurable timeout (use default like built-in checks)
- Check if command executable exists on server before accepting check
- Use functools.partial instead of nested function in run_retests()
- Add custom_check_runs to mock fixtures in test files
@rnetser rnetser closed this Jan 5, 2026
@rnetser rnetser reopened this Jan 5, 2026
@rnetser rnetser closed this Jan 5, 2026
@rnetser rnetser reopened this Jan 5, 2026
Clean up remnants from old logging patterns not removed during structured
logging migration. Replaces logger.step (undefined) and format_task_fields
(undefined) with standard logger.info calls.

Affected handlers:
- pull_request_handler.py: setup tasks and CI/CD tasks logging
- runner_handler.py: custom check execution logging
- Remove "variable name only" env var option, require explicit VAR=value format
- Consolidate command validation in _validate_custom_check_runs
- Remove redundant COMPLETED_STR from custom check success/failure methods
- Add tests for validation edge cases and duration formatting
- Achieve 90% test coverage
When passing env to asyncio.create_subprocess_exec(), it REPLACES the
entire environment instead of extending it. Without os.environ.copy(),
the subprocess wouldn't have PATH, HOME, or other essential variables,
causing commands like 'uv', 'python', etc. to fail.

- Change env_dict from empty dict to os.environ.copy()
- Add explanatory comment for future maintainers
- Update tests to verify environment inheritance
Copy link
Copy Markdown

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
webhook_server/libs/handlers/runner_handler.py (1)

429-449: HIGH: Wrap pull_request.title access in asyncio.to_thread() to avoid blocking the event loop.

PyGithub property accesses are synchronous and can trigger API calls. Line 434 and line 444 access pull_request.title directly in an async method without wrapping, which blocks the event loop. Fetch the title once via asyncio.to_thread() and reuse it.

Suggested fix
-        output: CheckRunOutput = {
+        title = await asyncio.to_thread(lambda: pull_request.title)
+        output: CheckRunOutput = {
             "title": "Conventional Title",
             "summary": "PR title follows Conventional Commits format",
             "text": (
                 f"**Format:** `<type>[optional scope]: <description>`\n\n"
-                f"**Your title:** `{pull_request.title}`\n\n"
+                f"**Your title:** `{title}`\n\n"
                 f"This title complies with the Conventional Commits v1.0.0 specification."
             ),
         }

         if await self.check_run_handler.is_check_run_in_progress(check_run=CONVENTIONAL_TITLE_STR):
             self.logger.info(f"{self.log_prefix} Check run is in progress, re-running {CONVENTIONAL_TITLE_STR}.")

         await self.check_run_handler.set_check_in_progress(name=CONVENTIONAL_TITLE_STR)
         allowed_names = [name.strip() for name in self.github_webhook.conventional_title.split(",") if name.strip()]
-        title = pull_request.title
+        # title already fetched above

This violates the coding guideline: "PyGithub is synchronous/blocking — MUST wrap ALL method calls and property accesses with asyncio.to_thread() to avoid blocking the event loop."

webhook_server/tests/test_github_api.py (1)

94-106: Fix return type annotation to include list type.
The function returns [] for "custom-check-runs" but the return type annotation excludes lists. This breaks strict mypy type checking and prevents catching real errors. Per coding guidelines, complete type hints are mandatory in strict mode.

Update the return annotation to include the list type:

Proposed fix
-        def _get_value_side_effect(value: str, *_args: object, **_kwargs: object) -> bool | dict[str, object] | None:
+        def _get_value_side_effect(
+            value: str, *_args: object, **_kwargs: object
+        ) -> bool | dict[str, object] | list[dict[str, object]] | None:
🤖 Fix all issues with AI agents
In `@webhook_server/tests/test_custom_check_runs.py`:
- Around line 605-723: Replace the manual prefix-slicing assertions with an
integration-style exercise of the real normalization: instantiate
IssueCommentHandler (using the mock_github_webhook and a Mock for its
dependencies), call its process_retest_command (or the public method that
handles comment bodies) with a comment_body like "/retest custom:lint", and
assert that RunnerHandler.run_custom_check (or RunnerHandler.run_custom_check
mocked on the RunnerHandler instance used by the handler) is called with
check_config/name "lint"; this ensures the production normalization logic in
IssueCommentHandler.process_retest_command is exercised rather than duplicating
string slicing in the test.
♻️ Duplicate comments (2)
webhook_server/libs/handlers/runner_handler.py (2)

192-249: CRITICAL: Avoid logging raw commands and fail-closed on unexpected errors.
Line 226 logs the full command before any redaction; custom checks can embed tokens, so this leaks secrets. Also, if an unexpected exception occurs after set_check_in_progress, the check can remain stuck in_progress. Suppress raw command logging and mark failure on unexpected exceptions.

🔧 Suggested hardening
-            cmd = check_config.command.replace("{worktree_path}", worktree_path)
-            self.logger.debug(f"{self.log_prefix} {check_config.name} command to run: {cmd}")
+            cmd = check_config.command.replace("{worktree_path}", worktree_path)
+            self.logger.debug(
+                f"{self.log_prefix} {check_config.name} command prepared (logging suppressed)"
+            )

             # Execute command - use cwd if configured, otherwise command should include paths
             cwd = worktree_path if check_config.use_cwd else None
             try:
                 rc, out, err = await run_command(
                     command=cmd,
                     log_prefix=self.log_prefix,
                     mask_sensitive=self.github_webhook.mask_sensitive,
                     cwd=cwd,
                 )
             except TimeoutError:
                 self.logger.error(f"{self.log_prefix} Check {check_config.name} timed out")
                 output["text"] = "Command execution timed out"
                 return await self.check_run_handler.set_check_failure(name=check_config.name, output=output)
+            except Exception as ex:
+                self.logger.exception(
+                    f"{self.log_prefix} Unexpected failure running {check_config.name}: {ex}"
+                )
+                output["text"] = str(ex)
+                await self.check_run_handler.set_check_failure(name=check_config.name, output=output)
+                raise

604-644: HIGH: Propagate asyncio.CancelledError and log tracebacks for retest failures.
gather(..., return_exceptions=True) turns cancellations into results and the current logger.error drops tracebacks. Re-raise cancellations and log full exception context to keep shutdown semantics and debugging intact.

🔧 Suggested fix
         results = await asyncio.gather(*tasks, return_exceptions=True)
         for result in results:
+            if isinstance(result, asyncio.CancelledError):
+                raise result
             if isinstance(result, Exception):
-                self.logger.error(f"{self.log_prefix} Async task failed: {result}")
+                self.logger.exception(f"{self.log_prefix} Async task failed", exc_info=result)
As per coding guidelines, always re-raise `asyncio.CancelledError`.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 162b93b and 7bf86bc.

📒 Files selected for processing (5)
  • webhook_server/libs/github_api.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/tests/conftest.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/test_github_api.py
🧰 Additional context used
📓 Path-based instructions (5)
webhook_server/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

webhook_server/**/*.py: Server fails-fast on startup if critical dependencies are missing - required parameters in __init__() are ALWAYS provided and checking for None on required parameters is pure overhead
Defensive checks in destructors (__del__) should use hasattr() to check for attributes that may not exist if initialization failed
Defensive checks are acceptable for optional parameters that explicitly allow None (parameter type includes | None or Optional)
Defensive checks are acceptable for lazy initialization - attribute explicitly starts as None with type annotation attr: SomeType | None = None
Use hasattr() checks only for platform constants that may not exist on all platforms (e.g., os.O_NOFOLLOW)
Do NOT use hasattr() checks for required parameters in __init__() - if config is required, access it directly without defensive checks
Do NOT use hasattr() checks for library versions we control in pyproject.toml (PyGithub >=2.4.0, gql >=3.5.0) - call methods directly
Use isinstance() for type discrimination instead of hasattr() checks
Never return fake defaults (empty strings, zeros, False, None, empty collections) to hide missing data - fail-fast with ValueError or KeyError exceptions
PyGithub is synchronous/blocking - MUST wrap ALL method calls and property accesses with asyncio.to_thread() to avoid blocking the event loop
When accessing PyGithub properties that may trigger API calls, wrap in asyncio.to_thread(lambda: obj.property) instead of direct access
When iterating PyGithub PaginatedList, wrap iteration in asyncio.to_thread(lambda: list(...)) to avoid blocking
Use asyncio.gather() for concurrent PyGithub operations to maximize performance
All imports must be at the top of files - no imports in functions or try/except blocks, except TYPE_CHECKING imports can be conditional
Complete type hints are mandatory - use mypy strict mode with full type annotations on function parameters and return types
Use logger.exception() for autom...

Files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/conftest.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
webhook_server/tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

webhook_server/tests/**/*.py: Code coverage must be 90% or higher - new code without tests fails CI
Test files should use AsyncMock, Mock, and patch for mocking - use TEST_GITHUB_TOKEN constant for test tokens
Test coverage is required for new handlers - create handler tests in webhook_server/tests/test_*_handler.py

Files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/conftest.py
webhook_server/libs/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Internal methods in webhook_server/libs/ can change freely - return types can change (e.g., Anybool), method signatures can be modified without deprecation

Files:

  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
webhook_server/libs/handlers/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

webhook_server/libs/handlers/**/*.py: Do NOT use defensive checks for architecture guarantees like repository_data which is ALWAYS set before handlers instantiate
Handler classes must implement __init__(self, github_webhook, ...) and process_event(event_data) pattern
Handlers must use self.github_webhook.unified_api for all GitHub operations, not direct PyGithub calls
Use ctx.start_step(), ctx.complete_step(), ctx.fail_step() for structured webhook execution tracking

Files:

  • webhook_server/libs/handlers/runner_handler.py
webhook_server/libs/github_api.py

📄 CodeRabbit inference engine (CLAUDE.md)

webhook_server/libs/github_api.py: Maintain backward compatibility for webhook payload handling - must follow GitHub webhook specification
Do NOT use defensive checks for webhook payload fields that are guaranteed to exist (e.g., user.node_id, user.type, sender) - let KeyError surface legitimate bugs
Repository cloning for check_run events should skip with early exit when action != 'completed'
Repository cloning for check_run events should skip when check_run_conclusion != 'success' and check_run_name == 'can-be-merged'

Files:

  • webhook_server/libs/github_api.py
🧠 Learnings (44)
📓 Common learnings
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/github_api.py : Repository cloning for check_run events should skip when check_run_conclusion != 'success' and check_run_name == 'can-be-merged'
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/tests/**/*.py : Test files should use AsyncMock, Mock, and patch for mocking - use TEST_GITHUB_TOKEN constant for test tokens

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/conftest.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/tests/**/*.py : Test coverage is required for new handlers - create handler tests in `webhook_server/tests/test_*_handler.py`

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/conftest.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/config/schema.yaml : Configuration schema changes should be tested with `uv run pytest webhook_server/tests/test_config_schema.py -v`

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/conftest.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/tests/**/*.py : Code coverage must be 90% or higher - new code without tests fails CI

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/conftest.py
  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/github_api.py : Repository cloning for check_run events should skip when check_run_conclusion != 'success' and check_run_name == 'can-be-merged'

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Defensive checks are acceptable for optional parameters that explicitly allow None (parameter type includes `| None` or `Optional`)

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Complete type hints are mandatory - use mypy strict mode with full type annotations on function parameters and return types

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
📚 Learning: 2024-10-08T09:19:56.185Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 586
File: webhook_server_container/libs/github_api.py:1947-1956
Timestamp: 2024-10-08T09:19:56.185Z
Learning: In `webhook_server_container/libs/github_api.py`, the indentation style used in the `set_pull_request_automerge` method is acceptable as per the project's coding standards.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2024-10-29T08:09:57.157Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 612
File: webhook_server_container/libs/github_api.py:2089-2100
Timestamp: 2024-10-29T08:09:57.157Z
Learning: In `webhook_server_container/libs/github_api.py`, when the function `_keep_approved_by_approvers_after_rebase` is called, existing approval labels have already been cleared after pushing new changes, so there's no need to check for existing approvals within this function.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Defensive checks are acceptable for lazy initialization - attribute explicitly starts as None with type annotation `attr: SomeType | None = None`

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/github_api.py
📚 Learning: 2025-10-30T00:18:06.176Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 878
File: webhook_server/libs/github_api.py:111-118
Timestamp: 2025-10-30T00:18:06.176Z
Learning: In webhook_server/libs/github_api.py, when creating temporary directories or performing operations that need repository names, prefer using self.repository_name (from webhook payload, always available) over dereferencing self.repository.name or self.repository_by_github_app.name, which may be None. This avoids AttributeError and keeps the code simple and reliable.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/github_api.py : Repository cloning for check_run events should skip with early exit when action != 'completed'

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2024-10-15T10:37:45.791Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 598
File: webhook_server_container/libs/github_api.py:1860-1874
Timestamp: 2024-10-15T10:37:45.791Z
Learning: In the `process_retest_command` method in `webhook_server_container/libs/github_api.py`, `_target_tests` is defined before use.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/conftest.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2024-10-09T09:16:45.452Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 579
File: webhook_server_container/libs/github_api.py:1098-1101
Timestamp: 2024-10-09T09:16:45.452Z
Learning: In the Python method `_run_tox` within `webhook_server_container/libs/github_api.py`, the variable `_tox_tests` is already comma-separated, so removing spaces with `_tox_tests.replace(" ", "")` is appropriate to handle any accidental spaces when specifying Tox environments.

Applied to files:

  • webhook_server/tests/test_github_api.py
📚 Learning: 2024-10-14T14:13:21.316Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 588
File: webhook_server_container/libs/github_api.py:1632-1637
Timestamp: 2024-10-14T14:13:21.316Z
Learning: In the `ProcessGithubWehook` class in `webhook_server_container/libs/github_api.py`, avoid using environment variables to pass tokens because multiple commands with multiple tokens can run at the same time.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/conftest.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Store GitHub tokens in environment variables or secret management systems, never commit tokens to repository

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/conftest.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/handlers/**/*.py : Handlers must use `self.github_webhook.unified_api` for all GitHub operations, not direct PyGithub calls

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/conftest.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/github_api.py : Maintain backward compatibility for webhook payload handling - must follow GitHub webhook specification

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/conftest.py
  • webhook_server/libs/github_api.py
📚 Learning: 2024-11-26T14:30:22.906Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 631
File: webhook_server_container/tests/test_github_api.py:321-327
Timestamp: 2024-11-26T14:30:22.906Z
Learning: In `webhook_server_container/tests/test_github_api.py`, when using `pytest.mark.parametrize` with indirect parameters, the `pytest.param` function may require nested lists to match the expected input structure.

Applied to files:

  • webhook_server/tests/test_github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/handlers/**/*.py : Handler classes must implement `__init__(self, github_webhook, ...)` and `process_event(event_data)` pattern

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/conftest.py
📚 Learning: 2025-12-29T13:09:56.231Z
Learnt from: rnetser
Repo: myk-org/github-webhook-server PR: 959
File: webhook_server/libs/handlers/pull_request_handler.py:887-887
Timestamp: 2025-12-29T13:09:56.231Z
Learning: In the myk-org/github-webhook-server repository, logging statements in the webhook_server codebase should use f-strings for interpolation. Do not suggest changing to lazy formatting with % (e.g., logger.debug('msg %s', var)) since that is an established style choice. When adding new log statements, prefer f"...{var}..." for readability and performance, and avoid string concatenation or format-style logging unless the project explicitly adopts a different convention.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/conftest.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2025-10-28T16:09:08.689Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 0
File: :0-0
Timestamp: 2025-10-28T16:09:08.689Z
Learning: For this repository, prioritize speed and minimizing API calls in reviews and suggestions: reuse webhook payload data, batch GraphQL queries, cache IDs (labels/users), and avoid N+1 patterns.

Applied to files:

  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/**/*.py : Internal methods in `webhook_server/libs/` can change freely - return types can change (e.g., `Any` → `bool`), method signatures can be modified without deprecation

Applied to files:

  • webhook_server/tests/test_custom_check_runs.py
📚 Learning: 2025-10-28T13:04:00.466Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 878
File: webhook_server/libs/handlers/runner_handler.py:491-571
Timestamp: 2025-10-28T13:04:00.466Z
Learning: In webhook_server/libs/handlers/runner_handler.py, the run_build_container method is designed with the pattern that push=True is always called with set_check=False in production code, so no check-run status needs to be finalized after push operations.

Applied to files:

  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/app.py : Use `create_context()` to initialize webhook context in app.py with hook_id, event_type, repository, action, sender, api_user

Applied to files:

  • webhook_server/tests/conftest.py
  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2024-11-21T16:10:01.777Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 624
File: webhook_server_container/libs/github_api.py:2067-2101
Timestamp: 2024-11-21T16:10:01.777Z
Learning: In `webhook_server_container/libs/github_api.py`, keep the `max_owners_files` limit as a hardcoded value rather than making it configurable through the config.

Applied to files:

  • webhook_server/tests/conftest.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Do NOT use hasattr() checks for library versions we control in `pyproject.toml` (PyGithub >=2.4.0, gql >=3.5.0) - call methods directly

Applied to files:

  • webhook_server/tests/conftest.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/github_api.py : Do NOT use defensive checks for webhook payload fields that are guaranteed to exist (e.g., `user.node_id`, `user.type`, `sender`) - let KeyError surface legitimate bugs

Applied to files:

  • webhook_server/tests/conftest.py
  • webhook_server/libs/github_api.py
📚 Learning: 2024-10-29T10:42:50.163Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 612
File: webhook_server_container/libs/github_api.py:925-926
Timestamp: 2024-10-29T10:42:50.163Z
Learning: In `webhook_server_container/libs/github_api.py`, the method `self._keep_approved_by_approvers_after_rebase()` must be called after removing labels when synchronizing a pull request. Therefore, it should be placed outside the `ThreadPoolExecutor` to ensure it runs sequentially after label removal.

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : PyGithub is synchronous/blocking - MUST wrap ALL method calls and property accesses with `asyncio.to_thread()` to avoid blocking the event loop

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : When accessing PyGithub properties that may trigger API calls, wrap in `asyncio.to_thread(lambda: obj.property)` instead of direct access

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : When iterating PyGithub PaginatedList, wrap iteration in `asyncio.to_thread(lambda: list(...))` to avoid blocking

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2025-05-13T12:06:27.297Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 778
File: webhook_server/libs/pull_request_handler.py:327-330
Timestamp: 2025-05-13T12:06:27.297Z
Learning: In the GitHub webhook server, synchronous GitHub API calls (like create_issue_comment, add_to_assignees, etc.) in async methods should be awaited using asyncio.to_thread or loop.run_in_executor to prevent blocking the event loop.

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Use `asyncio.gather()` for concurrent PyGithub operations to maximize performance

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Always re-raise `asyncio.CancelledError` after catching it

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/handlers/**/*.py : Do NOT use defensive checks for architecture guarantees like `repository_data` which is ALWAYS set before handlers instantiate

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2025-05-10T21:39:34.243Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 773
File: webhook_server/libs/pull_request_handler.py:281-308
Timestamp: 2025-05-10T21:39:34.243Z
Learning: In the refactored codebase, the `run_podman_command` method in `RunnerHandler` returns a tuple `(bool, str, str)` where the boolean `True` represents success and `False` represents failure, which is the opposite of traditional shell exit codes.

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2024-10-15T13:18:38.590Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 602
File: webhook_server_container/libs/github_api.py:2053-2062
Timestamp: 2024-10-15T13:18:38.590Z
Learning: In the `run_podman_command` method in `webhook_server_container/libs/github_api.py`, the `rc` variable is a boolean that returns `True` when the command succeeds and `False` when it fails.

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2025-02-25T12:01:42.999Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 666
File: webhook_server_container/libs/github_api.py:2254-2255
Timestamp: 2025-02-25T12:01:42.999Z
Learning: In the ProcessGithubWehook class, the `self.conventional_title` attribute is set during initialization by the `_repo_data_from_config()` method, and doesn't need additional existence checks.

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Access configuration using `Config(repository='org/repo-name')` and `config.get_value('setting-name', default_value)`

Applied to files:

  • webhook_server/libs/github_api.py
📚 Learning: 2024-10-14T14:12:28.924Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 588
File: webhook_server_container/libs/github_api.py:1638-1639
Timestamp: 2024-10-14T14:12:28.924Z
Learning: In `webhook_server_container/libs/github_api.py`, it is acceptable for `self.repository.owner.email` to be `None` since internal commands can handle this case.

Applied to files:

  • webhook_server/libs/github_api.py
📚 Learning: 2025-05-10T22:07:53.544Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 773
File: webhook_server/libs/pull_request_handler.py:553-557
Timestamp: 2025-05-10T22:07:53.544Z
Learning: In the GitHub webhook server codebase, `root_approvers` and `root_reviewers` are properties of the GithubWebhook class, not methods, so they can be used with `.copy()` without parentheses.

Applied to files:

  • webhook_server/libs/github_api.py
📚 Learning: 2025-10-31T13:07:23.714Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 878
File: webhook_server/libs/graphql/unified_api.py:333-341
Timestamp: 2025-10-31T13:07:23.714Z
Learning: In this repository (myk-org/github-webhook-server), the maintainer prefers not to clamp or validate user-provided configuration values (e.g., GraphQL query limits). Users should be allowed to set any value they want, and if it causes API failures, they can learn from the error and adjust the configuration themselves. Do not suggest adding validation or clamping logic for config values.

Applied to files:

  • webhook_server/libs/github_api.py
🧬 Code graph analysis (2)
webhook_server/libs/handlers/runner_handler.py (1)
webhook_server/libs/handlers/check_run_handler.py (6)
  • CheckRunHandler (39-440)
  • CheckRunOutput (31-36)
  • is_check_run_in_progress (250-256)
  • set_check_in_progress (122-130)
  • set_check_failure (143-152)
  • set_check_success (132-141)
webhook_server/libs/github_api.py (1)
webhook_server/libs/config.py (1)
  • get_value (132-153)
🪛 Ruff (0.14.11)
webhook_server/tests/conftest.py

18-18: Possible hardcoded password assigned to: "TEST_GITHUB_TOKEN"

(S105)

webhook_server/libs/handlers/runner_handler.py

206-206: Logging statement uses f-string

(G004)


208-208: Logging statement uses f-string

(G004)


219-219: Logging statement uses f-string

(G004)


226-226: Logging statement uses f-string

(G004)


238-238: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


238-238: Logging statement uses f-string

(G004)


245-245: Logging statement uses f-string

(G004)


248-248: Logging statement uses f-string

(G004)


253-253: Logging statement uses f-string

(G004)


274-274: Logging statement uses f-string

(G004)


362-362: Logging statement uses f-string

(G004)


446-446: Logging statement uses f-string

(G004)


612-612: Logging statement uses f-string

(G004)


635-635: Logging statement uses f-string

(G004)


637-637: Logging statement uses f-string

(G004)


644-644: Logging statement uses f-string

(G004)

webhook_server/libs/github_api.py

900-901: Logging statement uses f-string

(G004)


926-926: Logging statement uses f-string

(G004)


938-938: Logging statement uses f-string

(G004)


944-944: Logging statement uses f-string

(G004)


949-949: Logging statement uses f-string

(G004)


954-954: Logging statement uses f-string

(G004)


961-961: Logging statement uses f-string

(G004)


967-968: Logging statement uses f-string

(G004)


973-973: Logging statement uses f-string

(G004)


980-980: Logging statement uses f-string

(G004)


986-986: Logging statement uses f-string

(G004)


992-994: Logging statement uses f-string

(G004)


1001-1001: Logging statement uses f-string

(G004)


1005-1005: Logging statement uses f-string

(G004)


1007-1007: Logging statement uses f-string

(G004)

🔇 Additional comments (21)
webhook_server/tests/conftest.py (1)

17-18: LOW: Centralized TEST_GITHUB_TOKEN is a good test hook.
Single source of truth keeps mocks consistent across tests; the allowlist pragma is appropriate for a fake token. Based on learnings, this matches the test-mocking convention.

webhook_server/libs/github_api.py (3)

657-662: LOW: Custom check runs are loaded and validated in the right place.
Centralizing validation during config load keeps downstream handlers deterministic.


855-861: LOW: Custom checks are correctly included in supported retests.
This keeps /retest discovery aligned with validated configuration.


878-1009: LOW: Validation pipeline is robust and user-friendly.
Type guards, safe-name regex, duplicate detection, shlex parsing, and executable checks cover common misconfigs without crashing.

webhook_server/libs/handlers/runner_handler.py (4)

35-50: LOW: CheckConfig provides a clean, typed contract for check execution.
slots+frozen reduce accidental mutation and keep the runner API consistent.


251-280: LOW: Unified CheckConfig usage is consistent for tox/pre-commit/module install.
This keeps lifecycle logic centralized and reduces duplicated check handling.

Also applies to: 421-423


309-364: LOW: Container check status updates align with the generic check API.
The CheckRunOutput wiring and status transitions are consistent with the new pattern.


488-512: The schema does not support env as a separate field for custom checks.

The schema at webhook_server/config/schema.yaml lines 408–424 defines custom-check-runs items with additionalProperties: false, which explicitly forbids fields beyond name, command, and mandatory. The schema intentionally requires environment variables to be embedded directly in the command string (as shown in the examples: TOKEN=xyz DEBUG=true uv tool run --from bandit bandit -r .), not as a separate env field.

The test fixture in test_config_schema.py line 86 includes an "env" field, but:

  1. This violates the schema constraint (additionalProperties: false)
  2. The Config class does not validate against the schema—it only loads YAML
  3. The env field is silently ignored, not processed

No code changes are needed. Environment variables should remain embedded in the command string as the schema design intends.

webhook_server/tests/test_github_api.py (5)

17-17: LOW: Centralized test-token usage looks good.
This keeps token usage consistent and avoids scattered literals.


679-680: LOW: Config stub now covers custom checks.
Good to include "custom-check-runs" in the mocked config to keep the test aligned with new behavior.


1496-1501: LOW: No additional feedback on this block.


1980-2042: LOW: Collision validation test is solid.
Covers all built-in collisions and asserts the warning messages clearly.


2043-2090: LOW: Duplicate-name validation test is solid.
Good coverage of “first occurrence wins” behavior and warning emission.

webhook_server/tests/test_custom_check_runs.py (8)

1-33: LOW: Clear module docstring and focused imports.
Helps future maintainers understand the scope and intent quickly.


36-151: LOW: Validation coverage is comprehensive.
These tests exercise both happy-path and invalid-config cases with realistic command parsing.


153-269: LOW: CheckRunHandler custom-check status tests look good.
Covers queued, in-progress, success/failure, and required-status aggregation.


271-382: LOW: Mandatory/optional behavior is well covered.
Nice separation of “required for merge” vs “still executed” logic.


384-531: LOW: RunnerHandler custom-check execution tests look good.
The worktree handling and status transitions are thoroughly exercised.


533-603: LOW: Integration workflow test is clear and focused.
Good coverage of the end-to-end custom check flow.


725-1031: LOW: Validation logic edge cases are well covered.
Great breadth across naming rules, whitespace commands, and executable detection.


1033-1147: LOW: Edge-case coverage looks solid.
Timeout handling and long-command cases are exercised clearly.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread webhook_server/tests/test_custom_check_runs.py
- Fix security leak in run_check() by removing command debug log
- Handle CancelledError properly in run_retests()
- Move strip() to command source for efficiency
- Add shared BUILTIN_CHECK_NAMES constant
- Update schema example to use uv
- Improve test coverage and reuse fixtures
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jan 16, 2026
Copy link
Copy Markdown

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
webhook_server/libs/handlers/runner_handler.py (1)

443-463: Wrap pull_request.title access in asyncio.to_thread() to avoid blocking the event loop.

PyGithub property access is synchronous and can trigger network I/O. In async handlers, this blocks the event loop. Extract the title once before constructing the output dict and reuse the cached value:

title = await asyncio.to_thread(lambda: pull_request.title)
output: CheckRunOutput = {
    "title": "Conventional Title",
    "summary": "PR title follows Conventional Commits format",
    "text": (
        f"**Format:** `<type>[optional scope]: <description>`\n\n"
        f"**Your title:** `{title}`\n\n"
        f"This title complies with the Conventional Commits v1.0.0 specification."
    ),
}

This also eliminates the redundant property access at line 458.

🤖 Fix all issues with AI agents
In `@webhook_server/libs/handlers/runner_handler.py`:
- Around line 655-663: The retest logging misattributes failures because it
indexes supported_retests by results order; instead build and use an explicit
list of scheduled test names aligned with tasks (e.g., create scheduled_tests =
[] when you append each task to tasks) so you can reference scheduled_tests[idx]
when reporting errors; update the block using asyncio.gather(*tasks,
return_exceptions=True) to iterate results and use scheduled_tests (not
supported_retests) for the test_name in the logger calls (keep handling of
asyncio.CancelledError and re-raising unchanged).
♻️ Duplicate comments (1)
webhook_server/config/schema.yaml (1)

394-400: MEDIUM: Remove token-like example from custom-check-runs docs.
Config files are often committed; showing TOKEN=xyz normalizes storing secrets in plaintext and risks accidental leakage even with masking. Use a non-secret placeholder or omit the env assignment in examples. Based on learnings, avoid token env examples because parallel commands can collide and secrets shouldn’t be embedded in config.

💡 Suggested doc fix
-              - name: security-scan
-                command: TOKEN=xyz DEBUG=true uv tool run --from bandit bandit -r .
+              - name: security-scan
+                command: DEBUG=true uv tool run --from bandit bandit -r .
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7bf86bc and cd650fc.

📒 Files selected for processing (8)
  • webhook_server/config/schema.yaml
  • webhook_server/libs/github_api.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/tests/test_config_schema.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/test_github_api.py
  • webhook_server/utils/constants.py
  • webhook_server/utils/github_repository_settings.py
🧰 Additional context used
📓 Path-based instructions (7)
webhook_server/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

webhook_server/**/*.py: Server fails-fast on startup if critical dependencies are missing - required parameters in __init__() are ALWAYS provided and checking for None on required parameters is pure overhead
Defensive checks in destructors (__del__) should use hasattr() to check for attributes that may not exist if initialization failed
Defensive checks are acceptable for optional parameters that explicitly allow None (parameter type includes | None or Optional)
Defensive checks are acceptable for lazy initialization - attribute explicitly starts as None with type annotation attr: SomeType | None = None
Use hasattr() checks only for platform constants that may not exist on all platforms (e.g., os.O_NOFOLLOW)
Do NOT use hasattr() checks for required parameters in __init__() - if config is required, access it directly without defensive checks
Do NOT use hasattr() checks for library versions we control in pyproject.toml (PyGithub >=2.4.0, gql >=3.5.0) - call methods directly
Use isinstance() for type discrimination instead of hasattr() checks
Never return fake defaults (empty strings, zeros, False, None, empty collections) to hide missing data - fail-fast with ValueError or KeyError exceptions
PyGithub is synchronous/blocking - MUST wrap ALL method calls and property accesses with asyncio.to_thread() to avoid blocking the event loop
When accessing PyGithub properties that may trigger API calls, wrap in asyncio.to_thread(lambda: obj.property) instead of direct access
When iterating PyGithub PaginatedList, wrap iteration in asyncio.to_thread(lambda: list(...)) to avoid blocking
Use asyncio.gather() for concurrent PyGithub operations to maximize performance
All imports must be at the top of files - no imports in functions or try/except blocks, except TYPE_CHECKING imports can be conditional
Complete type hints are mandatory - use mypy strict mode with full type annotations on function parameters and return types
Use logger.exception() for autom...

Files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/utils/constants.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
  • webhook_server/tests/test_config_schema.py
  • webhook_server/utils/github_repository_settings.py
webhook_server/tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

webhook_server/tests/**/*.py: Code coverage must be 90% or higher - new code without tests fails CI
Test files should use AsyncMock, Mock, and patch for mocking - use TEST_GITHUB_TOKEN constant for test tokens
Test coverage is required for new handlers - create handler tests in webhook_server/tests/test_*_handler.py

Files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/test_config_schema.py
**/*.{yaml,yml}

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain backward compatibility for user-facing configuration files (config.yaml, .github-webhook-server.yaml) - configuration schema changes must support old formats or provide migration

Files:

  • webhook_server/config/schema.yaml
webhook_server/config/schema.yaml

📄 CodeRabbit inference engine (CLAUDE.md)

webhook_server/config/schema.yaml: Mask sensitive data in logs by default (set mask-sensitive-data: true in configuration)
Configuration schema changes should be tested with uv run pytest webhook_server/tests/test_config_schema.py -v

Files:

  • webhook_server/config/schema.yaml
webhook_server/libs/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Internal methods in webhook_server/libs/ can change freely - return types can change (e.g., Anybool), method signatures can be modified without deprecation

Files:

  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
webhook_server/libs/handlers/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

webhook_server/libs/handlers/**/*.py: Do NOT use defensive checks for architecture guarantees like repository_data which is ALWAYS set before handlers instantiate
Handler classes must implement __init__(self, github_webhook, ...) and process_event(event_data) pattern
Handlers must use self.github_webhook.unified_api for all GitHub operations, not direct PyGithub calls
Use ctx.start_step(), ctx.complete_step(), ctx.fail_step() for structured webhook execution tracking

Files:

  • webhook_server/libs/handlers/runner_handler.py
webhook_server/libs/github_api.py

📄 CodeRabbit inference engine (CLAUDE.md)

webhook_server/libs/github_api.py: Maintain backward compatibility for webhook payload handling - must follow GitHub webhook specification
Do NOT use defensive checks for webhook payload fields that are guaranteed to exist (e.g., user.node_id, user.type, sender) - let KeyError surface legitimate bugs
Repository cloning for check_run events should skip with early exit when action != 'completed'
Repository cloning for check_run events should skip when check_run_conclusion != 'success' and check_run_name == 'can-be-merged'

Files:

  • webhook_server/libs/github_api.py
🧠 Learnings (46)
📓 Common learnings
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/github_api.py : Repository cloning for check_run events should skip when check_run_conclusion != 'success' and check_run_name == 'can-be-merged'
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/tests/**/*.py : Test files should use AsyncMock, Mock, and patch for mocking - use TEST_GITHUB_TOKEN constant for test tokens

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/tests/**/*.py : Test coverage is required for new handlers - create handler tests in `webhook_server/tests/test_*_handler.py`

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/tests/test_config_schema.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/config/schema.yaml : Configuration schema changes should be tested with `uv run pytest webhook_server/tests/test_config_schema.py -v`

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/config/schema.yaml
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/github_api.py
  • webhook_server/tests/test_config_schema.py
  • webhook_server/utils/github_repository_settings.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/tests/**/*.py : Code coverage must be 90% or higher - new code without tests fails CI

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/github_api.py : Repository cloning for check_run events should skip when check_run_conclusion != 'success' and check_run_name == 'can-be-merged'

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
  • webhook_server/utils/github_repository_settings.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Defensive checks are acceptable for optional parameters that explicitly allow None (parameter type includes `| None` or `Optional`)

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Complete type hints are mandatory - use mypy strict mode with full type annotations on function parameters and return types

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/github_api.py
📚 Learning: 2024-10-08T09:19:56.185Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 586
File: webhook_server_container/libs/github_api.py:1947-1956
Timestamp: 2024-10-08T09:19:56.185Z
Learning: In `webhook_server_container/libs/github_api.py`, the indentation style used in the `set_pull_request_automerge` method is acceptable as per the project's coding standards.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2024-10-29T08:09:57.157Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 612
File: webhook_server_container/libs/github_api.py:2089-2100
Timestamp: 2024-10-29T08:09:57.157Z
Learning: In `webhook_server_container/libs/github_api.py`, when the function `_keep_approved_by_approvers_after_rebase` is called, existing approval labels have already been cleared after pushing new changes, so there's no need to check for existing approvals within this function.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
  • webhook_server/utils/github_repository_settings.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Defensive checks are acceptable for lazy initialization - attribute explicitly starts as None with type annotation `attr: SomeType | None = None`

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/github_api.py
📚 Learning: 2025-10-30T00:18:06.176Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 878
File: webhook_server/libs/github_api.py:111-118
Timestamp: 2025-10-30T00:18:06.176Z
Learning: In webhook_server/libs/github_api.py, when creating temporary directories or performing operations that need repository names, prefer using self.repository_name (from webhook payload, always available) over dereferencing self.repository.name or self.repository_by_github_app.name, which may be None. This avoids AttributeError and keeps the code simple and reliable.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/github_api.py
📚 Learning: 2024-10-09T09:16:45.452Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 579
File: webhook_server_container/libs/github_api.py:1098-1101
Timestamp: 2024-10-09T09:16:45.452Z
Learning: In the Python method `_run_tox` within `webhook_server_container/libs/github_api.py`, the variable `_tox_tests` is already comma-separated, so removing spaces with `_tox_tests.replace(" ", "")` is appropriate to handle any accidental spaces when specifying Tox environments.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/tests/test_custom_check_runs.py
📚 Learning: 2024-10-14T14:13:21.316Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 588
File: webhook_server_container/libs/github_api.py:1632-1637
Timestamp: 2024-10-14T14:13:21.316Z
Learning: In the `ProcessGithubWehook` class in `webhook_server_container/libs/github_api.py`, avoid using environment variables to pass tokens because multiple commands with multiple tokens can run at the same time.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/config/schema.yaml
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2024-10-15T10:37:45.791Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 598
File: webhook_server_container/libs/github_api.py:1860-1874
Timestamp: 2024-10-15T10:37:45.791Z
Learning: In the `process_retest_command` method in `webhook_server_container/libs/github_api.py`, `_target_tests` is defined before use.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Store GitHub tokens in environment variables or secret management systems, never commit tokens to repository

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/handlers/**/*.py : Handlers must use `self.github_webhook.unified_api` for all GitHub operations, not direct PyGithub calls

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/github_api.py : Maintain backward compatibility for webhook payload handling - must follow GitHub webhook specification

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/libs/github_api.py
📚 Learning: 2024-11-26T14:30:22.906Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 631
File: webhook_server_container/tests/test_github_api.py:321-327
Timestamp: 2024-11-26T14:30:22.906Z
Learning: In `webhook_server_container/tests/test_github_api.py`, when using `pytest.mark.parametrize` with indirect parameters, the `pytest.param` function may require nested lists to match the expected input structure.

Applied to files:

  • webhook_server/tests/test_github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/handlers/**/*.py : Handler classes must implement `__init__(self, github_webhook, ...)` and `process_event(event_data)` pattern

Applied to files:

  • webhook_server/tests/test_github_api.py
📚 Learning: 2025-12-29T13:09:56.231Z
Learnt from: rnetser
Repo: myk-org/github-webhook-server PR: 959
File: webhook_server/libs/handlers/pull_request_handler.py:887-887
Timestamp: 2025-12-29T13:09:56.231Z
Learning: In the myk-org/github-webhook-server repository, logging statements in the webhook_server codebase should use f-strings for interpolation. Do not suggest changing to lazy formatting with % (e.g., logger.debug('msg %s', var)) since that is an established style choice. When adding new log statements, prefer f"...{var}..." for readability and performance, and avoid string concatenation or format-style logging unless the project explicitly adopts a different convention.

Applied to files:

  • webhook_server/tests/test_github_api.py
  • webhook_server/utils/constants.py
  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
  • webhook_server/tests/test_config_schema.py
  • webhook_server/utils/github_repository_settings.py
📚 Learning: 2025-10-28T16:09:08.689Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 0
File: :0-0
Timestamp: 2025-10-28T16:09:08.689Z
Learning: For this repository, prioritize speed and minimizing API calls in reviews and suggestions: reuse webhook payload data, batch GraphQL queries, cache IDs (labels/users), and avoid N+1 patterns.

Applied to files:

  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/**/*.py : Internal methods in `webhook_server/libs/` can change freely - return types can change (e.g., `Any` → `bool`), method signatures can be modified without deprecation

Applied to files:

  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/utils/github_repository_settings.py
📚 Learning: 2025-10-28T13:04:00.466Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 878
File: webhook_server/libs/handlers/runner_handler.py:491-571
Timestamp: 2025-10-28T13:04:00.466Z
Learning: In webhook_server/libs/handlers/runner_handler.py, the run_build_container method is designed with the pattern that push=True is always called with set_check=False in production code, so no check-run status needs to be finalized after push operations.

Applied to files:

  • webhook_server/tests/test_custom_check_runs.py
  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2024-10-29T10:42:50.163Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 612
File: webhook_server_container/libs/github_api.py:925-926
Timestamp: 2024-10-29T10:42:50.163Z
Learning: In `webhook_server_container/libs/github_api.py`, the method `self._keep_approved_by_approvers_after_rebase()` must be called after removing labels when synchronizing a pull request. Therefore, it should be placed outside the `ThreadPoolExecutor` to ensure it runs sequentially after label removal.

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : PyGithub is synchronous/blocking - MUST wrap ALL method calls and property accesses with `asyncio.to_thread()` to avoid blocking the event loop

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : When accessing PyGithub properties that may trigger API calls, wrap in `asyncio.to_thread(lambda: obj.property)` instead of direct access

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : When iterating PyGithub PaginatedList, wrap iteration in `asyncio.to_thread(lambda: list(...))` to avoid blocking

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2025-05-13T12:06:27.297Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 778
File: webhook_server/libs/pull_request_handler.py:327-330
Timestamp: 2025-05-13T12:06:27.297Z
Learning: In the GitHub webhook server, synchronous GitHub API calls (like create_issue_comment, add_to_assignees, etc.) in async methods should be awaited using asyncio.to_thread or loop.run_in_executor to prevent blocking the event loop.

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Use `asyncio.gather()` for concurrent PyGithub operations to maximize performance

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Always re-raise `asyncio.CancelledError` after catching it

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/github_api.py : Repository cloning for check_run events should skip with early exit when action != 'completed'

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/libs/handlers/**/*.py : Do NOT use defensive checks for architecture guarantees like `repository_data` which is ALWAYS set before handlers instantiate

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/app.py : Use `create_context()` to initialize webhook context in app.py with hook_id, event_type, repository, action, sender, api_user

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2025-05-10T21:39:34.243Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 773
File: webhook_server/libs/pull_request_handler.py:281-308
Timestamp: 2025-05-10T21:39:34.243Z
Learning: In the refactored codebase, the `run_podman_command` method in `RunnerHandler` returns a tuple `(bool, str, str)` where the boolean `True` represents success and `False` represents failure, which is the opposite of traditional shell exit codes.

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2024-10-15T13:18:38.590Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 602
File: webhook_server_container/libs/github_api.py:2053-2062
Timestamp: 2024-10-15T13:18:38.590Z
Learning: In the `run_podman_command` method in `webhook_server_container/libs/github_api.py`, the `rc` variable is a boolean that returns `True` when the command succeeds and `False` when it fails.

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2025-02-25T12:01:42.999Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 666
File: webhook_server_container/libs/github_api.py:2254-2255
Timestamp: 2025-02-25T12:01:42.999Z
Learning: In the ProcessGithubWehook class, the `self.conventional_title` attribute is set during initialization by the `_repo_data_from_config()` method, and doesn't need additional existence checks.

Applied to files:

  • webhook_server/libs/handlers/runner_handler.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Do NOT use hasattr() checks for library versions we control in `pyproject.toml` (PyGithub >=2.4.0, gql >=3.5.0) - call methods directly

Applied to files:

  • webhook_server/libs/github_api.py
  • webhook_server/utils/github_repository_settings.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Access configuration using `Config(repository='org/repo-name')` and `config.get_value('setting-name', default_value)`

Applied to files:

  • webhook_server/libs/github_api.py
  • webhook_server/utils/github_repository_settings.py
📚 Learning: 2024-11-21T16:10:01.777Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 624
File: webhook_server_container/libs/github_api.py:2067-2101
Timestamp: 2024-11-21T16:10:01.777Z
Learning: In `webhook_server_container/libs/github_api.py`, keep the `max_owners_files` limit as a hardcoded value rather than making it configurable through the config.

Applied to files:

  • webhook_server/libs/github_api.py
📚 Learning: 2024-10-14T14:12:28.924Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 588
File: webhook_server_container/libs/github_api.py:1638-1639
Timestamp: 2024-10-14T14:12:28.924Z
Learning: In `webhook_server_container/libs/github_api.py`, it is acceptable for `self.repository.owner.email` to be `None` since internal commands can handle this case.

Applied to files:

  • webhook_server/libs/github_api.py
📚 Learning: 2025-05-10T22:07:53.544Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 773
File: webhook_server/libs/pull_request_handler.py:553-557
Timestamp: 2025-05-10T22:07:53.544Z
Learning: In the GitHub webhook server codebase, `root_approvers` and `root_reviewers` are properties of the GithubWebhook class, not methods, so they can be used with `.copy()` without parentheses.

Applied to files:

  • webhook_server/libs/github_api.py
📚 Learning: 2025-10-31T13:07:23.714Z
Learnt from: myakove
Repo: myk-org/github-webhook-server PR: 878
File: webhook_server/libs/graphql/unified_api.py:333-341
Timestamp: 2025-10-31T13:07:23.714Z
Learning: In this repository (myk-org/github-webhook-server), the maintainer prefers not to clamp or validate user-provided configuration values (e.g., GraphQL query limits). Users should be allowed to set any value they want, and if it causes API failures, they can learn from the error and adjust the configuration themselves. Do not suggest adding validation or clamping logic for config values.

Applied to files:

  • webhook_server/libs/github_api.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Use hasattr() checks only for platform constants that may not exist on all platforms (e.g., `os.O_NOFOLLOW`)

Applied to files:

  • webhook_server/libs/github_api.py
  • webhook_server/utils/github_repository_settings.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : Do NOT use hasattr() checks for required parameters in `__init__()` - if config is required, access it directly without defensive checks

Applied to files:

  • webhook_server/utils/github_repository_settings.py
📚 Learning: 2026-01-05T14:43:44.055Z
Learnt from: CR
Repo: myk-org/github-webhook-server PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-05T14:43:44.055Z
Learning: Applies to webhook_server/**/*.py : All imports must be at the top of files - no imports in functions or try/except blocks, except TYPE_CHECKING imports can be conditional

Applied to files:

  • webhook_server/utils/github_repository_settings.py
🧬 Code graph analysis (2)
webhook_server/tests/test_custom_check_runs.py (3)
webhook_server/libs/github_api.py (1)
  • _validate_custom_check_runs (879-1003)
webhook_server/libs/handlers/check_run_handler.py (6)
  • CheckRunHandler (39-440)
  • set_check_queued (111-120)
  • set_check_in_progress (122-130)
  • set_check_success (132-141)
  • set_check_failure (143-152)
  • all_required_status_checks (355-390)
webhook_server/libs/handlers/runner_handler.py (2)
  • RunnerHandler (53-663)
  • run_custom_check (502-526)
webhook_server/libs/handlers/runner_handler.py (1)
webhook_server/libs/handlers/check_run_handler.py (6)
  • CheckRunOutput (31-36)
  • is_check_run_in_progress (250-256)
  • set_check_in_progress (122-130)
  • get_check_run_text (192-248)
  • set_check_failure (143-152)
  • set_check_success (132-141)
🪛 Ruff (0.14.11)
webhook_server/libs/handlers/runner_handler.py

207-207: Logging statement uses f-string

(G004)


209-209: Logging statement uses f-string

(G004)


220-220: Logging statement uses f-string

(G004)


239-239: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


239-239: Logging statement uses f-string

(G004)


246-246: Logging statement uses f-string

(G004)


249-249: Logging statement uses f-string

(G004)


253-253: Logging statement uses f-string

(G004)


256-256: Logging statement uses f-string

(G004)


267-267: Logging statement uses f-string

(G004)


288-288: Logging statement uses f-string

(G004)


376-376: Logging statement uses f-string

(G004)


460-460: Logging statement uses f-string

(G004)


626-626: Logging statement uses f-string

(G004)


649-649: Logging statement uses f-string

(G004)


651-651: Logging statement uses f-string

(G004)


658-658: Logging statement uses f-string

(G004)


663-663: Logging statement uses f-string

(G004)

webhook_server/libs/github_api.py

901-902: Logging statement uses f-string

(G004)


917-917: Logging statement uses f-string

(G004)


929-929: Logging statement uses f-string

(G004)


935-935: Logging statement uses f-string

(G004)


940-940: Logging statement uses f-string

(G004)


945-945: Logging statement uses f-string

(G004)


952-952: Logging statement uses f-string

(G004)


958-959: Logging statement uses f-string

(G004)


967-967: Logging statement uses f-string

(G004)


974-974: Logging statement uses f-string

(G004)


980-980: Logging statement uses f-string

(G004)


986-988: Logging statement uses f-string

(G004)


995-995: Logging statement uses f-string

(G004)


999-999: Logging statement uses f-string

(G004)


1001-1001: Logging statement uses f-string

(G004)

🔇 Additional comments (26)
webhook_server/tests/test_config_schema.py (2)

81-88: LOW: Good coverage of custom-check-runs in the full config fixture.
This keeps the schema change exercised end-to-end in config loading. As per coding guidelines, schema changes should be tested.


142-153: LOW: Assertions for custom-check-runs structure look solid.
Validates required fields and the default behavior for mandatory.

webhook_server/utils/constants.py (1)

129-137: LOW: Centralized BUILTIN_CHECK_NAMES improves consistency.
Single source of truth makes collision checks and settings updates safer.

webhook_server/utils/github_repository_settings.py (1)

19-27: LOW: Using BUILTIN_CHECK_NAMES and a frozenset signature is clean and consistent.
Reduces duplication and keeps built-in checks aligned across call sites.

Also applies to: 334-361

webhook_server/tests/test_github_api.py (5)

17-17: LOW: Using TEST_GITHUB_TOKEN aligns with test token guidelines.
Keeps token usage centralized and consistent. As per coding guidelines.


94-106: LOW: custom-check-runs handled in config side-effect mock.
Prevents unrelated clone tests from breaking on the new config key.


650-681: LOW: custom-check-runs coverage added to the add‑api‑users config mock.
Keeps the mock aligned with the new config path.


1478-1497: LOW: Reusing the shared get_value_side_effect keeps mocks consistent.
Avoids brittle tests as Config keys evolve.


1975-2085: LOW: Strong tests for built‑in collisions and duplicate custom checks.
Validates filtering behavior and warning logs without touching real executables.

webhook_server/libs/github_api.py (3)

658-663: LOW: LGTM — custom checks are loaded via config and validated up front.
Good use of a centralized validation hook before wiring into runtime flows.


855-862: LOW: LGTM — custom checks are appended to supported retest targets.
Keeps /retest behavior aligned with repo config.


879-1003: LOW: LGTM — validator is thorough (names, duplicates, env-prefixes, executables).
The guardrails and summary logging make misconfigurations easy to diagnose.

webhook_server/tests/test_custom_check_runs.py (8)

36-151: LOW: LGTM — validation tests cover happy path + missing fields.
Clear assertions for required keys and command parsing behavior.


153-270: LOW: LGTM — CheckRunHandler custom check status methods are exercised well.
Good coverage of queued/in_progress/success/failure semantics.


271-383: LOW: LGTM — mandatory flag behavior and queueing rules are covered.
Nice separation of “required for merge” vs “executed for all.”


384-531: LOW: LGTM — runner flow tests cover success, failure, checkout, and cwd usage.
These cases map directly to the unified run_check lifecycle.


533-604: LOW: LGTM — integration flow validates end-to-end custom check execution.
Verifies the expected in_progress → success sequence.


605-701: LOW: LGTM — retest command/name semantics match config expectations.
Confirms raw names are used without prefixes.


702-1009: LOW: LGTM — validator edge cases (unsafe names, missing executables, env-prefix) are solid.
Good breadth for regression coverage.


1010-1124: LOW: LGTM — edge-case behavior for “no checks” and timeouts is covered.
Ensures graceful failure paths remain tested.

webhook_server/libs/handlers/runner_handler.py (6)

35-50: LOW: LGTM — CheckConfig cleanly encapsulates per-check metadata.
Makes the unified run_check API straightforward to reuse.


192-263: LOW: LGTM — unified run_check flow is solid with timeout + error handling.
Good lifecycle sequencing and cancellation safety.


270-294: LOW: LGTM — built-in checks now delegate consistently through CheckConfig.
This reduces drift between built-ins and custom checks.

Also applies to: 435-437


323-378: LOW: LGTM — container build now uses unified check status updates.
Keeps status reporting consistent with other checks.


502-526: LOW: LGTM — custom checks wrap into unified run_check with cwd execution.
Keeps validation centralized in GithubWebhook.


542-613: LOW: LGTM — cherry-pick check status updates are wired consistently.
Matches the unified check-run reporting style.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread webhook_server/libs/handlers/runner_handler.py
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jan 16, 2026
@rnetser
Copy link
Copy Markdown
Collaborator Author

rnetser commented Jan 18, 2026

/build-and-push-container

@myakove-bot
Copy link
Copy Markdown
Collaborator

New container for ghcr.io/myk-org/github-webhook-server:pr-961 published

Comment thread webhook_server/config/schema.yaml
Comment thread webhook_server/libs/handlers/check_run_handler.py Outdated
Comment thread webhook_server/libs/handlers/check_run_handler.py
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jan 18, 2026
@rnetser
Copy link
Copy Markdown
Collaborator Author

rnetser commented Jan 18, 2026

/build-and-push-container

@myakove-bot
Copy link
Copy Markdown
Collaborator

New container for ghcr.io/myk-org/github-webhook-server:pr-961 published

Copy link
Copy Markdown

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
webhook_server/libs/handlers/runner_handler.py (2)

444-464: HIGH: Wrap pull_request.title access in asyncio.to_thread() to prevent blocking the event loop.

PyGithub properties are synchronous and can trigger API calls. Direct access to pull_request.title in the async method blocks the event loop. Per repo guidelines, fetch the value once and reuse it.

🔧 Suggested fix
-        output: CheckRunOutput = {
+        title = await asyncio.to_thread(lambda: pull_request.title)
+        output: CheckRunOutput = {
             "title": "Conventional Title",
             "summary": "PR title follows Conventional Commits format",
             "text": (
                 f"**Format:** `<type>[optional scope]: <description>`\n\n"
-                f"**Your title:** `{pull_request.title}`\n\n"
+                f"**Your title:** `{title}`\n\n"
                 f"This title complies with the Conventional Commits v1.0.0 specification."
             ),
         }
@@
-        title = pull_request.title
+        # `title` already fetched via asyncio.to_thread above

539-604: Wrap unprotected PyGithub property accesses in asyncio.to_thread() to prevent event-loop blocking.

Lines 543, 551–553, and 602 access pull_request properties directly without wrapping. These synchronous PyGithub operations block the event loop. Fetch all properties once via asyncio.gather() and reuse the values, especially at lines 551–553 where multiple properties are accessed sequentially.

🔧 Suggested fix
-        new_branch_name = f"{CHERRY_PICKED_LABEL}-{pull_request.head.ref}-{shortuuid.uuid()[:5]}"
+        head_ref = await asyncio.to_thread(lambda: pull_request.head.ref)
+        new_branch_name = f"{CHERRY_PICKED_LABEL}-{head_ref}-{shortuuid.uuid()[:5]}"
@@
-            commit_hash = pull_request.merge_commit_sha
-            commit_msg_striped = pull_request.title.replace("'", "")
-            pull_request_url = pull_request.html_url
+            commit_hash, pr_title, pull_request_url = await asyncio.gather(
+                asyncio.to_thread(lambda: pull_request.merge_commit_sha),
+                asyncio.to_thread(lambda: pull_request.title),
+                asyncio.to_thread(lambda: pull_request.html_url),
+            )
+            commit_msg_striped = pr_title.replace("'", "")
@@
-                        local_branch_name = f"{pull_request.head.ref}-{target_branch}"
+                        local_branch_name = f"{head_ref}-{target_branch}"

@rnetser
Copy link
Copy Markdown
Collaborator Author

rnetser commented Jan 18, 2026

/verified

  • retest command
  • welcome message
  • check is added as optional to PRs

@myakove
Copy link
Copy Markdown
Collaborator

myakove commented Jan 19, 2026

rebase so we can merge it @rnetser

@rnetser
Copy link
Copy Markdown
Collaborator Author

rnetser commented Jan 19, 2026

/verified

@myakove
Copy link
Copy Markdown
Collaborator

myakove commented Jan 19, 2026

/lgtm
/approve

@myakove-bot
Copy link
Copy Markdown
Collaborator

Successfully removed PR tag: ghcr.io/myk-org/github-webhook-server:pr-961.

@myakove-bot
Copy link
Copy Markdown
Collaborator

New container for ghcr.io/myk-org/github-webhook-server:latest published

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants