Skip to content

fix: authenticate GitHub API requests with GH_TOKEN#191

Open
jaeyeom wants to merge 1 commit intogabyx:mainfrom
jaeyeom:fix-authenticate-github-api
Open

fix: authenticate GitHub API requests with GH_TOKEN#191
jaeyeom wants to merge 1 commit intogabyx:mainfrom
jaeyeom:fix-authenticate-github-api

Conversation

@jaeyeom
Copy link
Contributor

@jaeyeom jaeyeom commented Feb 17, 2026

Unauthenticated GitHub API requests are limited to 60 req/hr per IP, which causes "403 API rate limit exceeded" errors during installation. Authenticated requests get 5,000 req/hr. This change uses the GH_TOKEN environment variable (when set) to add an Authorization header to all GitHub API requests made during install and update.

Two call sites are patched:

  1. GetFile() in download.go: replaced http.Get() with http.NewRequest + http.DefaultClient.Do() so we can attach the Bearer token header. This covers all asset and checksum downloads (used by github.go, gitea.go, and checksums.go).

  2. downloadGithub() in github.go: the go-github client now receives a custom *http.Client with a tokenTransport RoundTripper that injects the Authorization header on every request. When GH_TOKEN is unset, nil is passed, preserving the existing default-client behavior.

This avoids adding an oauth2 dependency by using a lightweight RoundTripper wrapper instead.

Unauthenticated GitHub API requests are limited to 60 req/hr per IP,
which causes "403 API rate limit exceeded" errors during installation.
Authenticated requests get 5,000 req/hr. This change uses the GH_TOKEN
environment variable (when set) to add an Authorization header to all
GitHub API requests made during install and update.

Two call sites are patched:

1. GetFile() in download.go: replaced http.Get() with http.NewRequest +
   http.DefaultClient.Do() so we can attach the Bearer token header.
   This covers all asset and checksum downloads (used by github.go,
   gitea.go, and checksums.go).

2. downloadGithub() in github.go: the go-github client now receives a
   custom *http.Client with a tokenTransport RoundTripper that injects
   the Authorization header on every request. When GH_TOKEN is unset,
   nil is passed, preserving the existing default-client behavior.

This avoids adding an oauth2 dependency by using a lightweight
RoundTripper wrapper instead.
@jaeyeom
Copy link
Contributor Author

jaeyeom commented Feb 18, 2026

This is a kind of minimum change. But accessing an environment variable deep in a library package an anti-pattern, since env var is like a global variable. It makes library functions impure, harder to test, etc. So if more diff lines is acceptable, I also have another change that accesses OS env var in the main package.

Copy link
Owner

@gabyx gabyx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this addition: could we just use the newest Gh module? v50 to keep it even cleaner ?

Otherwise looks very clean.

Maybe add a Docs entry or a comment at the appropriate deploy.yaml
which is either in the docs or somewhere (?).

@jaeyeom
Copy link
Contributor Author

jaeyeom commented Feb 20, 2026

Thanks for this addition: could we just use the newest Gh module? v50 to keep it even cleaner ?

Otherwise looks very clean.

Maybe add a Docs entry or a comment at the appropriate deploy.yaml which is either in the docs or somewhere (?).

Yes. I think making this up to the latest version make sense. I'll create another PR to see if there'a any broken changes, to isolate with this feature.

@gabyx
Copy link
Owner

gabyx commented Feb 21, 2026

@jaeyeom : Jeah accessing the env. var is kind of ugly inside the package. I guess since this code is only used in git hooks update it might work:

Also to say is that I probably did not account properly for secrets like this PR introduces etc in the deploy.yaml

Cause any deploy.yaml should not have a token inside (IMO) which is a bit dangerous, I would probably:

  • Just implicit read it as GITHUB_TOKEN in github.go, in the Downlaod function.

  • Or then we enter the pandoras box of questioning the user to enter the Token or

  • we get it from git credential get when cwd is inside the release checkout in ~/.githooks/release.
    the first checkout on ~/.githooks/release anyway must work (git hooks installer) cause if you install from a private repo (fork) you need to have a working Git which can clone from this location.

Option 3 is really complicated, we might go with the first pragmatic approach and maybe we can on failing download -> print an error message that the user might set GITHUB_TOKEN if the requests are blocked.

@gabyx
Copy link
Owner

gabyx commented Feb 21, 2026

@jaeyeom : rebase for fixing the linting changes, probably =)

@jaeyeom
Copy link
Contributor Author

jaeyeom commented Feb 23, 2026

@jaeyeom : Jeah accessing the env. var is kind of ugly inside the package. I guess since this code is only used in git hooks update it might work:

Also to say is that I probably did not account properly for secrets like this PR introduces etc in the deploy.yaml

Cause any deploy.yaml should not have a token inside (IMO) which is a bit dangerous, I would probably:

  • Just implicit read it as GITHUB_TOKEN in github.go, in the Downlaod function.
  • Or then we enter the pandoras box of questioning the user to enter the Token or
  • we get it from git credential get when cwd is inside the release checkout in ~/.githooks/release.
    the first checkout on ~/.githooks/release anyway must work (git hooks installer) cause if you install from a private repo (fork) you need to have a working Git which can clone from this location.

Option 3 is really complicated, we might go with the first pragmatic approach and maybe we can on failing download -> print an error message that the user might set GITHUB_TOKEN if the requests are blocked.

That's why I raised that concern from the very beginning. Maybe you've missed it. :)

#191 (comment)

Option 3 is really complicated, we might go with the first pragmatic approach and maybe we can on failing download -> print an error message that the user might set GITHUB_TOKEN if the requests are blocked.

I agree with you on your approach. Github already shows the error message in their REST API about this.

A good news that, as you already know, GH_TOKEN is optional. If users aren't calling github APIs from the same IP multiple times, it is generally fine. It's still problematic when the user use a kind of update script that updates several packages at the same time, VPN or cloud environment, this can be a problem. In such case, I think showing a good error message would let the user know that they can set the env var.

So I guess we have consensus on the option 1, using env var. Did I understand it correctly? Option 2 is probably additional feature for interactive shell, but lower priority in my opinion. Option 3 is overly complicated as you said. I guess you can set secret to github like the following without exposing it in deploy.yml file or so, if you think this solution is fine.

# Interactive prompt
gh secret set GH_TOKEN

# From a file
gh secret set GH_TOKEN < token.txt

I'll create another PR for reading env var in the main package, which touches more files and bigger diffs, but better in terms of architecture.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants