(input: string)
| 85 | * Note: repo names can contain dots (e.g., cc.kurs.web) |
| 86 | */ |
| 87 | export function parseGitRemote(input: string): ParsedRepository | null { |
| 88 | const trimmed = input.trim() |
| 89 | |
| 90 | // SSH format: git@host:owner/repo.git |
| 91 | const sshMatch = trimmed.match(/^git@([^:]+):([^/]+)\/([^/]+?)(?:\.git)?$/) |
| 92 | if (sshMatch?.[1] && sshMatch[2] && sshMatch[3]) { |
| 93 | if (!looksLikeRealHostname(sshMatch[1])) return null |
| 94 | return { |
| 95 | host: sshMatch[1], |
| 96 | owner: sshMatch[2], |
| 97 | name: sshMatch[3], |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | // URL format: https://host/owner/repo.git, ssh://git@host/owner/repo, git://host/owner/repo |
| 102 | const urlMatch = trimmed.match( |
| 103 | /^(https?|ssh|git):\/\/(?:[^@]+@)?([^/:]+(?::\d+)?)\/([^/]+)\/([^/]+?)(?:\.git)?$/, |
| 104 | ) |
| 105 | if (urlMatch?.[1] && urlMatch[2] && urlMatch[3] && urlMatch[4]) { |
| 106 | const protocol = urlMatch[1] |
| 107 | const hostWithPort = urlMatch[2] |
| 108 | const hostWithoutPort = hostWithPort.split(':')[0] ?? '' |
| 109 | if (!looksLikeRealHostname(hostWithoutPort)) return null |
| 110 | // Only preserve port for HTTPS — SSH/git ports are not usable for constructing |
| 111 | // web URLs (e.g. ssh://git@ghe.corp.com:2222 → port 2222 is SSH, not HTTPS). |
| 112 | const host = |
| 113 | protocol === 'https' || protocol === 'http' |
| 114 | ? hostWithPort |
| 115 | : hostWithoutPort |
| 116 | return { |
| 117 | host, |
| 118 | owner: urlMatch[3], |
| 119 | name: urlMatch[4], |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | return null |
| 124 | } |
| 125 | |
| 126 | /** |
| 127 | * Parses a git remote URL or "owner/repo" string and returns "owner/repo". |
no test coverage detected