discourse/plugins/discourse-github
Régis Hanol 36a8a51ef0
FEATURE: Route all GitHub API requests through one rate-limited client (#40637)
GitHub oneboxes and the discourse-github plugin talked to GitHub's REST
and
GraphQL API with no rate-limit awareness. On busy instances this
exhausted
GitHub's limits (60 requests/hour unauthenticated, 5000 authenticated),
and
because there was no backoff every render kept hitting GitHub and
re-failing
-- which GitHub's docs warn can get an integration banned. The recently
added PR-status onebox multiplied the number of calls and made it far
worse.

GitHub access was also fragmented: the core onebox engines used OpenURI,
the
discourse-github plugin used Octokit, and the discourse-ai bot tools
used
FinalDestination::HTTP -- three HTTP stacks, three tokens, and
inconsistent
(or entirely missing) error and rate-limit handling.

This introduces a single client, Discourse::GithubApi, that every GitHub
data-API request now flows through. It is built on Faraday with the
SSRF-safe
FinalDestination adapter and:

- authenticates per token (Bearer) and returns plain string-keyed Hashes
(get/post) or raw bodies (raw_get) -- one response shape, no
Octokit/Sawyer
- only ever sends the access token to api.github.com and
  raw.githubusercontent.com, rejecting any other absolute URL, so a
  user-derived path can never leak a token to an arbitrary host
- backs off on rate limits both reactively (403/429) and proactively
(when
X-RateLimit-Remaining hits 0), honouring Retry-After /
X-RateLimit-Reset,
via a shared Redis flag (GithubRateLimit) keyed per token so each
token's
  budget and the shared unauthenticated/IP budget back off independently
- short-circuits while backing off without ever sleeping, so onebox
rendering
  and post baking degrade to a plain link instead of blocking a request
- caches ETags and sends If-None-Match, so unchanged resources return
304s
  that do not count against the rate limit

Every caller was moved onto it:

- the 6 core GitHub onebox engines, via a slimmed
Onebox::Mixins::GithubApi
adapter that keeps their public methods and translates client errors
back
to the OpenURI::HTTPError vocabulary they already rescue (engines
unchanged)
- the github_blob raw.githubusercontent.com fetch
- the discourse-github plugin (badges, linkback, permalinks, token
validator),
which no longer uses the octokit and sawyer gems (they stay in the
Gemfile for
the discourse-code-review official plugin, which still depends on them)
- the discourse-ai bot's GitHub tools (search code, diff, file content,
  search files)

Also adds a GithubOneboxBackoff admin problem check that surfaces while
one of
the onebox token identities is backing off -- scoped to the tokens
resolved by
Onebox::GithubAccess (each configured github_onebox_access_tokens entry
plus the
unauthenticated client) so a backoff on the AI bot or linkback token is
not
misattributed to onebox. Its message points admins at the relevant
setting with
the {{setting:...}} link marker, which problem-check messages now expand
too.
Onebox token resolution is centralised in Onebox::GithubAccess, and the
onebox
cache TTL for transient GitHub failures is shortened so they recover
quickly.

GitHub OAuth login, theme git-clone, the inbound webhook, and the
Oneboxer
FinalDestination URL-resolution special-cases for github.com are
intentionally
out of scope -- they are different concerns, not the rate-limited data
API.
2026-06-15 10:59:10 +02:00
..
app FEATURE: Route all GitHub API requests through one rate-limited client (#40637) 2026-06-15 10:59:10 +02:00
assets FEATURE: Add PR status icon to inline oneboxes (#39649) 2026-05-04 18:15:14 +02:00
config I18N: Update translations (#40300) 2026-05-26 15:25:51 +02:00
db/migrate
spec FEATURE: Route all GitHub API requests through one rate-limited client (#40637) 2026-06-15 10:59:10 +02:00
package.json DEV: Add a script for generating external types in discourse-types (#37095) 2026-03-09 20:37:43 +01:00
plugin.rb FEATURE: Route all GitHub API requests through one rate-limited client (#40637) 2026-06-15 10:59:10 +02:00
README.md
tsconfig.json DEV: Add a script for generating external types in discourse-types (#37095) 2026-03-09 20:37:43 +01:00

Discourse Github

https://meta.discourse.org/t/discourse-github/99895/

This plugin combines functionality of deprecated github_badges and discourse-github-linkback plugins.

Installation

Follow the plugin installation guide.

Github Badges

Assign badges to your users based on GitHub contributions.

How to use:

  1. Enable github badges enabled in Settings -> Plugins.

  2. Generate an access token on Github. Be sure to give it only the public_repo scope. Paste that token into the github linkback access token setting.

  3. Add URL of the GitHub repo to scan for contributions to the github badges repo site setting.

Github Linkback

Create a link from a Github pull request or commit back to a Discourse post where it is mentioned.

How to use:

  1. Enable github linkback enabled in Settings -> Plugins.

  2. Generate an access token on Github. Be sure to give it only the public_repo scope. Paste that token into the github linkback access token setting.

  3. Finally, add the projects you wish to post to in the github linkback projects site setting in the formats:

    • username/repository for specific repositories
    • username/* for all repositories of a certain user

Replace Github non-permalinks with permalinks.

How to use:

  1. Enable github permalinks enabled in Settings -> Plugins.
  2. If you want to exclude certain files or directories from permalink overwrites, you can modify that in the github permalinks exclude site setting in the following formats:
    • filename links to all files with matching filename
    • username/* links to all repositories belonging to the user/organization
    • username/repository/* links to any file in the repository
    • username/repository/directory/* links to any file in the directory within repository
    • username/repository/file.rb a specific file