The GitHub linkback feature posts comments on PRs/issues/commits when
they are mentioned in Discourse posts. However, it never checked whether
the GitHub PR/issue/commit already contained a link back to the same
Discourse topic (in its description or existing comments). This led to
redundant linkback comments when a PR already referenced the topic.
Before posting a linkback comment, we now fetch the PR/issue body and
existing comments (or commit comments) from the GitHub API and check
whether any of them already contain a URL pointing to the same Discourse
topic. URL matching uses Discourse.route_for — the same mechanism used
by the oneboxer, search, and TopicLink — to reliably recognize all topic
URL formats (/t/slug/id, /t/id, /t/id/post_number, etc.).
On any GitHub API error, the check fails open (still posts the linkback)
to preserve the pre-existing behavior — and because if the GET fails,
the subsequent POST will likely fail too.
Sometimes you have categories where posts mention GitHub URLs but you
don't actually want linkback comments posted to GitHub — think
AI-generated summaries or automated reports that reference commits but
would just create noise on the issue tracker.
This adds a `github_linkback_ignored_categories` site setting that lets
admins specify which categories should be skipped entirely when
processing linkbacks. Posts in those categories won't trigger any GitHub
comments, keeping things clean.
To make this work nicely, the setting uses the `category_list` type
which gives you a proper category picker in the admin UI rather than
having to type raw IDs. This required extending core's `_map`
metaprogramming helper (which previously only worked with `group_list`
settings) to also handle `category_list` — so now any `category_list`
setting automatically gets a `_map` variant that returns an array of
integers, just like group settings do.
Screenshot of new setting:
<img width="774" height="116" alt="discourse-github-ignore-cat"
src="https://github.com/user-attachments/assets/dd9a3c85-7f01-47c6-b1b7-fe6ef120ab8b"
/>
Ref: /t/141863
---------
Co-authored-by: zogstrip <regis@hanol.fr>
Previously, when rebaking posts containing GitHub PR oneboxes, we passed
`invalidate_oneboxes: true` to `rebake!` which would re-fetch the onebox
during cooking. This meant each post independently fetched the same PR
data from GitHub.
Now we:
1. Fetch and cache the onebox once upfront via `Oneboxer.preview`
2. Use the new `skip_publish_rebaked_changes` parameter to prevent
publishing intermediate states, without triggering onebox re-fetches
3. Filter posts by `cooked LIKE '%githubpullrequest%'` in SQL rather
than fetching all linked posts and filtering in Ruby
The new `skip_publish_rebaked_changes` parameter on `Post#rebake!`
allows callers to skip the `:rebaked` publish without triggering onebox
invalidation, which is useful when the caller has already refreshed the
cache separately.
When a GitHub PR webhook triggers a rebake of chat messages containing
PR oneboxes, mentioned users were being re-notified. This happened
because the Chat::ProcessMessage job always invokes the notifier, unlike
regular posts where alerting is handled separately.
Adds a `skip_notifications` parameter to Chat::Message#rebake! that
propagates to the ProcessMessage job, which then skips calling the
Chat::Notifier. The GitHub webhook job now passes this flag when
rebaking chat messages to prevent duplicate notifications.
Internal ref - t/169442
Posts and chat messages with inline oneboxes to GitHub PRs were being
unnecessarily rebaked when the PR status changed. This happened because
the check `cooked.include?("onebox")` matched both full oneboxes
(`class="onebox githubpullrequest"`) and inline oneboxes
(`class="inline-onebox"`).
Changed the check to match "githubpullrequest" specifically, which only
appears in full PR oneboxes that actually display the status.
Internal ref - t/169442
Fix GitHub PR onebox updates not working for chat messages.
When a GitHub webhook triggered the `RebakeGithubPrPosts` job, chat
messages containing PR links were not being rebaked. The issue was
twofold:
1. `defined?(Chat::MessageLink)` doesn't trigger Rails autoloading - it
only checks if a constant is already loaded, so it would return `nil`
before the class was ever accessed
2. Inside `module Jobs`, Ruby's constant resolution would look for
`Jobs::Chat::MessageLink` before the global `::Chat::MessageLink`
- Replace `defined?(Chat)` check with `SiteSetting.chat_enabled` which
is the idiomatic Discourse pattern for checking plugin availability
- Remove the redundant `defined?(Chat::MessageLink)` guard since the
site setting check already handles this
- Add `::` prefix to `Chat::MessageLink` and `Chat::Message` references
to ensure they resolve to the global namespace
Internal ref - t/169442
I wanted a better way to show a live/up-to-date status of a PR than what
the https://github.com/discourse/github-status-theme provided.
First, I wanted it to work on any PR, including ones in private
repositories.
Second, I wanted to include the approved status, which requires a 2nd
call to GitHub's API.
Third, I wanted something that was less "intrusive" than the
"shield-like" status, so I opted out to use the icons & color that
GitHub uses (but maybe that's a bad idea).
After a first PoC that was a dump proxy to handle authentication for private repositories, the idea to use org webhooks was raised and turned out to be much lighter and easier than figuring out a proper caching strategy.
So this adds support for receiving `pull_request` and `pull_request_review` webhooks events to schedule a background job that will rebake posts and chat messages that have a link to the mentioned PR.
The GitHub PR onebox has been updated to query and parse the review state so we can have a better icon.
This also adds the `Chat::MessageLink` model (that is the #chat version of `TopicLink`) to help better keep track of links posted in chat.
**BEFORE**
<img width="1525" height="1356" alt="BEFORE"
src="https://github.com/user-attachments/assets/02f66137-9f4f-4a91-b321-73816161e204"
/>
**AFTER**
<img width="1525" height="1356" alt="AFTER"
src="https://github.com/user-attachments/assets/2164b7ef-8cd0-4cec-aea8-936a76a94fb2"
/>
Internal ref - t/17138