Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new GitHub bot that helps reviewers track PRs and issues that are waiting for author updates. The bot responds to /wait and /wait-any slash commands to add labels, and automatically removes these labels when the author pushes code or adds comments.
Key Changes
- Implements two wait modes:
/wait(removed on code push only) and/wait-any(removed on push or comment) - Automatically manages "waiting" and "waiting:any" labels based on PR/issue activity
- Provides visual feedback via emoji reactions on command execution
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| github_wait/handlers.py | Core event handlers for slash commands, PR sync, and comment events with automatic label management |
| github_wait/autokitteh.yaml | Configuration defining GitHub connection, event triggers, and filtering logic |
| github_wait/README.md | Comprehensive documentation covering features, usage, deployment, and implementation details |
| github_wait/Makefile | Build tooling for linting, formatting, type checking, and deployment |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| issue: _IssueOrPR = repo.get_issue(number=data.issue.number) | ||
| elif data.get("pull_request"): | ||
| issue = repo.get_pull(number=data.pull_request.number) |
There was a problem hiding this comment.
The variable name issue is used for both issues and pull requests, which can be confusing. Consider using a more generic name like item, target, or issue_or_pr to better reflect that it can hold either type. The type alias _IssueOrPR already exists and could inform a better name.
| if data.get("issue"): | ||
| issue: _IssueOrPR = repo.get_issue(number=data.issue.number) | ||
| elif data.get("pull_request"): | ||
| issue = repo.get_pull(number=data.pull_request.number) |
There was a problem hiding this comment.
The issue variable may be undefined if neither data.get("issue") nor data.get("pull_request") is truthy. This would cause a NameError when _label(issue, ...) is called on line 42. Consider adding an else clause to handle this case or ensure that at least one condition is always met based on the event filter.
| issue = repo.get_pull(number=data.pull_request.number) | |
| issue = repo.get_pull(number=data.pull_request.number) | |
| else: | |
| print("Error: neither issue nor pull_request found in event data") | |
| return |
| call: handlers.py:on_pull_request_review_comment | ||
| filter: "data.action == 'created'" | ||
|
|
||
| - # On a new review , remove wait-any labels unless the comment is a command. |
There was a problem hiding this comment.
Extra space before comma. Should be "review," not "review ,".
| - # On a new review , remove wait-any labels unless the comment is a command. | |
| - # On a new review, remove wait-any labels unless the comment is a command. |
| def on_issue_comment(event: Event) -> None: | ||
| data = event.data | ||
|
|
||
| if not any( | ||
| label["name"] in [_waiting_any_label, _waiting_push_label] | ||
| for label in data.issue.labels | ||
| ): | ||
| print("not waiting, ignored") | ||
| return | ||
|
|
||
| if data.slash_commands and any( | ||
| cmd["name"] in ["wait", "wait-any"] for cmd in data.slash_commands | ||
| ): | ||
| # Don't remove labels if this comment is a command. | ||
| print("comment contains wait command, ignored") | ||
| return | ||
|
|
||
| repo = github.get_repo(data.repository.full_name) | ||
| issue = repo.get_issue(data.issue.number) | ||
|
|
||
| # Remove only wait-any label on new comment. | ||
| _label(issue, adds=[], removes=[_waiting_any_label]) | ||
|
|
||
|
|
||
| def on_pull_request_review_comment(event: Event) -> None: | ||
| data = event.data | ||
|
|
||
| if not any( | ||
| label["name"] in [_waiting_any_label, _waiting_push_label] | ||
| for label in data.pull_request.labels | ||
| ): | ||
| print("not waiting, ignored") | ||
| return | ||
|
|
||
| if data.slash_commands and any( | ||
| cmd["name"] in ["wait", "wait-any"] for cmd in data.slash_commands | ||
| ): | ||
| # Don't remove labels if this comment is a command. | ||
| print("comment contains wait command, ignored") | ||
| return | ||
|
|
||
| repo = github.get_repo(data.repository.full_name) | ||
| pr = repo.get_pull(data.pull_request.number) | ||
|
|
||
| # Remove only wait-any label on new comment. | ||
| _label(pr, adds=[], removes=[_waiting_any_label]) | ||
|
|
||
|
|
||
| def on_pull_request_review(event: Event) -> None: | ||
| data = event.data | ||
|
|
||
| if not any( | ||
| label["name"] in [_waiting_any_label, _waiting_push_label] | ||
| for label in data.pull_request.labels | ||
| ): | ||
| print("not waiting, ignored") | ||
| return | ||
|
|
||
| if data.slash_commands and any( | ||
| cmd["name"] in ["wait", "wait-any"] for cmd in data.slash_commands | ||
| ): | ||
| # Don't remove labels if this comment is a command. | ||
| print("comment contains wait command, ignored") | ||
| return | ||
|
|
||
| repo = github.get_repo(data.repository.full_name) | ||
| pr = repo.get_pull(data.pull_request.number) | ||
|
|
||
| # Remove only wait-any label on new comment. | ||
| _label(pr, adds=[], removes=[_waiting_any_label]) |
There was a problem hiding this comment.
The functions on_issue_comment, on_pull_request_review_comment, and on_pull_request_review contain nearly identical logic (lines 57-78, 81-102, and 105-126). Consider extracting the common logic into a helper function that accepts the event data and the appropriate data field accessor (e.g., data.issue vs data.pull_request) to reduce duplication and improve maintainability.
| - **`waiting`**: Indicates waiting for code changes (push events only) | ||
| - **`waiting:any`**: Indicates waiting for any activity (comments or code changes) | ||
|
|
||
| These labels should be created in your repository. The bot will create them automatically on first use if they don't exist. |
There was a problem hiding this comment.
The documentation states "The bot will create them automatically on first use if they don't exist." However, the code in handlers.py uses issue.add_to_labels(add) without explicit label creation logic. Verify that PyGithub's add_to_labels() method automatically creates non-existent labels, or update the documentation to clarify that labels should be pre-created in the repository.
| These labels should be created in your repository. The bot will create them automatically on first use if they don't exist. | |
| **Important:** These labels must be created manually in your repository before using the bot. The bot does not create labels automatically. |
| - **Reviewer Comments**: When reviewers add more comments (including wait commands), labels stay in place | ||
| - **Author Comments**: Comments from the author remove the `waiting:any` label (but not `waiting`) | ||
| - **Code Pushes**: New commits from the author remove both `waiting` and `waiting:any` labels | ||
| - **Reviews**: New review submissions remove the `waiting:any` label | ||
|
|
||
| This means reviewers can continue discussing without accidentally removing the waiting state - only author activity triggers label removal. |
There was a problem hiding this comment.
The documentation states that "Reviewer Comments" keep labels in place and only "Author Comments" remove the waiting:any label. However, the implementation in handlers.py (lines 57-78, 81-102, 105-126) removes the waiting:any label on ANY comment (unless it contains a wait command), regardless of who posted it. The code does not differentiate between reviewer and author comments. Either the documentation should be updated to reflect the actual behavior, or the code should be modified to check the comment author and only remove labels for author comments.
| - **Reviewer Comments**: When reviewers add more comments (including wait commands), labels stay in place | |
| - **Author Comments**: Comments from the author remove the `waiting:any` label (but not `waiting`) | |
| - **Code Pushes**: New commits from the author remove both `waiting` and `waiting:any` labels | |
| - **Reviews**: New review submissions remove the `waiting:any` label | |
| This means reviewers can continue discussing without accidentally removing the waiting state - only author activity triggers label removal. | |
| - **Comments**: Any new comment (unless it contains a wait command) removes the `waiting:any` label, regardless of whether it is from the author or a reviewer. | |
| - **Code Pushes**: New commits from the author remove both `waiting` and `waiting:any` labels | |
| - **Reviews**: New review submissions remove the `waiting:any` label | |
| This means that any activity in the form of a comment will remove the `waiting:any` label, so reviewers and authors alike can trigger label removal. |
see README.md for details
implements https://github.com/repokitteh/modules/blob/master/wait.star