( owner: string, name: string, pkgVersion: string, )
| 213 | } |
| 214 | |
| 215 | async function fetchLatestReleaseFromGitHub( |
| 216 | owner: string, |
| 217 | name: string, |
| 218 | pkgVersion: string, |
| 219 | ): Promise<LatestRelease> { |
| 220 | const url = `https://api.github.com/repos/${owner}/${name}/releases/latest`; |
| 221 | const controller = new AbortController(); |
| 222 | const timeout = setTimeout(() => controller.abort(), 10_000); |
| 223 | |
| 224 | try { |
| 225 | let response: Response; |
| 226 | try { |
| 227 | response = await fetch(url, { |
| 228 | headers: { |
| 229 | Accept: 'application/vnd.github+json', |
| 230 | 'User-Agent': `xcodebuildmcp/${pkgVersion}`, |
| 231 | }, |
| 232 | signal: controller.signal, |
| 233 | }); |
| 234 | } catch (error) { |
| 235 | if (error instanceof Error && error.name === 'AbortError') { |
| 236 | throw new Error("couldn't determine latest version from GitHub: request timed out"); |
| 237 | } |
| 238 | const reason = error instanceof Error ? error.message : String(error); |
| 239 | throw new Error(`couldn't determine latest version from GitHub: ${reason}`); |
| 240 | } |
| 241 | |
| 242 | if (response.status === 403 || response.status === 429) { |
| 243 | throw new Error("couldn't determine latest version from GitHub: rate limit exceeded"); |
| 244 | } |
| 245 | |
| 246 | if (!response.ok) { |
| 247 | throw new Error(`couldn't determine latest version from GitHub: HTTP ${response.status}`); |
| 248 | } |
| 249 | |
| 250 | const data = (await response.json()) as GitHubReleaseResponse; |
| 251 | |
| 252 | if (!data.tag_name) { |
| 253 | throw new Error("couldn't determine latest version from GitHub: missing tag_name"); |
| 254 | } |
| 255 | |
| 256 | return { |
| 257 | tag: data.tag_name, |
| 258 | version: data.tag_name.startsWith('v') ? data.tag_name.slice(1) : data.tag_name, |
| 259 | }; |
| 260 | } finally { |
| 261 | clearTimeout(timeout); |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | interface LatestReleaseFetcherDeps { |
| 266 | repositoryOwner: string; |
no outgoing calls
no test coverage detected