-
Notifications
You must be signed in to change notification settings - Fork 3
feat: support user-defined custom commands in PR welcome message #1133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9048834
9ffaf4f
5e69b62
0bd51f3
dbd5785
4909358
c67389b
8b5f205
1b46cd1
3fe865d
3e232f3
a8e2782
73d3baa
b271db2
542bac6
9290524
97abc9a
7f2fdb4
18a055d
1e0486a
003eeb1
36c6adc
6b8ee6e
d25d699
feb76b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,6 +68,22 @@ $defs: | |
| - ai-provider | ||
| - ai-model | ||
| additionalProperties: false | ||
| custom-command-item: | ||
| type: object | ||
| properties: | ||
| name: | ||
| type: string | ||
| pattern: "^[a-zA-Z0-9_-]+$" | ||
| minLength: 1 | ||
| description: Command name (without the leading slash) | ||
| description: | ||
| type: string | ||
| minLength: 1 | ||
| description: Human-readable description of what the command does | ||
| required: | ||
| - name | ||
| - description | ||
| additionalProperties: false | ||
| security-checks: | ||
| type: object | ||
| description: | | ||
|
|
@@ -253,6 +269,15 @@ properties: | |
| $ref: '#/$defs/ai-features' | ||
| security-checks: | ||
| $ref: '#/$defs/security-checks' | ||
| custom-commands: | ||
|
rnetser marked this conversation as resolved.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [SUGGESTION] No maxLength/maxItems constraints on custom-commands The schema has no Suggestion: Consider adding |
||
| type: array | ||
| minItems: 1 | ||
| description: | | ||
| Custom commands to display in the PR welcome message (global default). | ||
| These are documentation-only - the server renders them in the welcome | ||
| message but does NOT process them. External bots or tools handle them. | ||
| items: | ||
| $ref: '#/$defs/custom-command-item' | ||
| labels: | ||
| type: object | ||
| description: Configure which labels are enabled and their colors | ||
|
|
@@ -716,3 +741,19 @@ properties: | |
| - name | ||
| - command | ||
| additionalProperties: false | ||
| custom-commands: | ||
| type: array | ||
| description: | | ||
|
rnetser marked this conversation as resolved.
|
||
| Custom commands to display in the PR welcome message (per-repo override). | ||
| These are documentation-only - the server renders them in the welcome | ||
| message but does NOT process them. External bots or tools handle them. | ||
| An empty list is allowed to explicitly disable global custom-commands | ||
| for this repository. | ||
|
|
||
| Examples: | ||
| - name: deploy-staging | ||
| description: Deploy this PR to the staging environment | ||
| - name: run-e2e | ||
| description: Trigger end-to-end test suite against this PR | ||
| items: | ||
| $ref: '#/$defs/custom-command-item' | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -497,6 +497,7 @@ def _prepare_welcome_comment(self) -> str: | |
| {self._prepare_retest_welcome_comment} | ||
| {self._prepare_container_operations_welcome_section}\ | ||
| {self._prepare_cherry_pick_section}\ | ||
| {self._prepare_custom_commands_welcome_section}\ | ||
|
|
||
| #### Label Management | ||
| * `/<label-name>` - Add a label to the PR | ||
|
|
@@ -842,6 +843,42 @@ def _prepare_cherry_pick_section(self) -> str: | |
| """ | ||
| return "\n#### Branch Management\n* `/rebase` - Rebase this PR branch onto its base branch\n" | ||
|
|
||
| @staticmethod | ||
| def _escape_markdown(text: str) -> str: | ||
| """Escape markdown special characters in text. | ||
|
|
||
| Prevents markdown injection when inserting user-provided text | ||
| into PR comments. Escapes characters that could create links, | ||
| images, inline code, bold, underline, strikethrough, or HTML tags, | ||
| and neutralizes @mentions. | ||
| """ | ||
| for char in ("[", "]", "(", ")", "!", "`", "*", "_", "~", "<", ">"): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [SUGGESTION] _escape_markdown missing backslash escape The backslash Suggestion: Add text = text.replace("\\", "\\\\")Low risk since descriptions come from admin config. |
||
| text = text.replace(char, f"\\{char}") | ||
| # Neutralize @mentions to prevent unintended user/team pings | ||
| text = text.replace("@", "@\u200b") | ||
| return text | ||
|
|
||
| @property | ||
| def _prepare_custom_commands_welcome_section(self) -> str: | ||
| """Prepare the Custom Commands section for the welcome comment. | ||
|
|
||
| Renders user-defined custom commands from configuration. | ||
| These are documentation-only - the server does not process them. | ||
| Commands are validated at load time by GithubWebhook._validate_custom_commands(). | ||
| """ | ||
| custom_commands: list[dict[str, str]] = self.github_webhook.custom_commands | ||
| if not custom_commands: | ||
| return "" | ||
|
|
||
| lines: list[str] = ["\n#### Custom Commands"] | ||
| for cmd in custom_commands: | ||
|
rnetser marked this conversation as resolved.
|
||
| name = cmd["name"] | ||
| description = cmd["description"] | ||
| sanitized = self._escape_markdown(description.replace("\n", " ").replace("\r", " ")) | ||
| lines.append(f"* `/{name}` - {sanitized}") | ||
|
|
||
| return "\n".join(lines) + "\n" | ||
|
|
||
| async def label_all_opened_pull_requests_merge_state_after_merged(self) -> None: | ||
| """ | ||
| Labels pull requests based on their mergeable state. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.