Support GitHub Gist URLs via HTTP redirects in uv run (#16451)

## Summary

Extend the existing GitHub Gist URL support from #15058 to handle URLs
that redirect to Gists.

`reqwest` already handled generic URL redirects for us (note this
redirects directly to the `.py` on `gist.githubusercontent.com`):

~/git/uv $ uv run
c28a4bf0cb/hello.py
    hello world!

But running a URL that redirected to a Gist's "main page" (a bit.ly link
leading to a Gist, etc.) did not:

~/git/uv $ uv run
4d878a4d95
      File "/tmp/scriptNodt3Q.py", line 87
        <title>hello.py · GitHub</title>

But if we have `reqwest` follow redirects *before* `resolve_gist_url`,
we can handle this fine:

~/git/uv $ target/debug/uv run
4d878a4d95
    hello world!

## Test Plan

I'd write an automated test but that'd require network access since
wiremock doesn't seem to support mocking specific hostnames like
`gist.github.com`. As manual tests go, I basically did the above,
testing with several redirectors to both generic and Gist URLs.
This commit is contained in:
twilligon 2025-10-29 09:30:22 -07:00 committed by GitHub
parent c6d0b412a0
commit db1d34e91b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1735,9 +1735,24 @@ impl RunCommand {
if !cfg!(unix) || matches!(target_path.try_exists(), Ok(false)) { if !cfg!(unix) || matches!(target_path.try_exists(), Ok(false)) {
let mut url = DisplaySafeUrl::parse(&target.to_string_lossy())?; let mut url = DisplaySafeUrl::parse(&target.to_string_lossy())?;
let client = client_builder.build();
let mut response = client
.for_host(&url)
.get(Url::from(url.clone()))
.send()
.await?;
// If it's a Gist URL, use the GitHub API to get the raw URL. // If it's a Gist URL, use the GitHub API to get the raw URL.
if url.host_str() == Some("gist.github.com") { if response.url().host_str() == Some("gist.github.com") {
url = resolve_gist_url(&url, &client_builder).await?; url =
resolve_gist_url(DisplaySafeUrl::ref_cast(response.url()), &client_builder)
.await?;
response = client
.for_host(&url)
.get(Url::from(url.clone()))
.send()
.await?;
} }
let file_stem = url let file_stem = url
@ -1750,13 +1765,6 @@ impl RunCommand {
.suffix(".py") .suffix(".py")
.tempfile()?; .tempfile()?;
let client = client_builder.build();
let response = client
.for_host(&url)
.get(Url::from(url.clone()))
.send()
.await?;
// Stream the response to the file. // Stream the response to the file.
let mut writer = file.as_file(); let mut writer = file.as_file();
let mut reader = response.bytes_stream(); let mut reader = response.bytes_stream();