|
4 | 4 |
|
5 | 5 | import asyncio |
6 | 6 | import base64 |
7 | | -import os |
8 | 7 | import re |
9 | 8 | from typing import Final |
10 | 9 | from urllib.parse import urlparse |
11 | 10 |
|
| 11 | +import httpx |
12 | 12 | from starlette.status import ( |
13 | 13 | HTTP_200_OK, |
14 | 14 | HTTP_301_MOVED_PERMANENTLY, |
@@ -115,46 +115,31 @@ async def check_repo_exists(url: str, token: str | None = None) -> bool: |
115 | 115 | If the host returns an unrecognised status code. |
116 | 116 |
|
117 | 117 | """ |
118 | | - # TODO: use `requests` instead of `curl` |
119 | | - cmd: list[str] = [ |
120 | | - "curl", |
121 | | - "--silent", # Suppress output |
122 | | - "--location", # Follow redirects |
123 | | - "--write-out", |
124 | | - "%{http_code}", # Write the HTTP status code to stdout |
125 | | - "-o", |
126 | | - os.devnull, |
127 | | - ] |
| 118 | + headers = {} |
128 | 119 |
|
129 | 120 | if token and is_github_host(url): |
130 | 121 | host, owner, repo = _parse_github_url(url) |
131 | 122 | # Public GitHub vs. GitHub Enterprise |
132 | 123 | base_api = "https://api.github.com" if host == "github.com" else f"https://{host}/api/v3" |
133 | 124 | url = f"{base_api}/repos/{owner}/{repo}" |
134 | | - cmd += ["--header", f"Authorization: Bearer {token}"] |
135 | | - |
136 | | - cmd.append(url) |
137 | | - |
138 | | - proc = await asyncio.create_subprocess_exec( |
139 | | - *cmd, |
140 | | - stdout=asyncio.subprocess.PIPE, |
141 | | - stderr=asyncio.subprocess.PIPE, |
142 | | - ) |
143 | | - stdout, _ = await proc.communicate() |
144 | | - |
145 | | - if proc.returncode != 0: |
146 | | - return False |
147 | | - |
148 | | - status = int(stdout.decode().strip()) |
149 | | - if status in {HTTP_200_OK, HTTP_301_MOVED_PERMANENTLY}: |
150 | | - return True |
151 | | - # TODO: handle 302 redirects |
152 | | - if status in {HTTP_404_NOT_FOUND, HTTP_302_FOUND}: |
153 | | - return False |
154 | | - if status in {HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN}: |
155 | | - return False |
156 | | - msg = f"Unexpected HTTP status {status} for {url}" |
157 | | - raise RuntimeError(msg) |
| 125 | + headers["Authorization"] = f"Bearer {token}" |
| 126 | + |
| 127 | + async with httpx.AsyncClient(follow_redirects=True) as client: |
| 128 | + try: |
| 129 | + response = await client.head(url, headers=headers) |
| 130 | + status = response.status_code |
| 131 | + |
| 132 | + if status in {HTTP_200_OK, HTTP_301_MOVED_PERMANENTLY}: |
| 133 | + return True |
| 134 | + # TODO: handle 302 redirects |
| 135 | + if status in {HTTP_404_NOT_FOUND, HTTP_302_FOUND}: |
| 136 | + return False |
| 137 | + if status in {HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN}: |
| 138 | + return False |
| 139 | + msg = f"Unexpected HTTP status {status} for {url}" |
| 140 | + raise RuntimeError(msg) |
| 141 | + except httpx.RequestError: |
| 142 | + return False |
158 | 143 |
|
159 | 144 |
|
160 | 145 | def _parse_github_url(url: str) -> tuple[str, str, str]: |
|
0 commit comments