// // Git remote URL parsing. Covers the four shapes Gitea hands out: HTTPS, SCP // style SSH, ssh:// URLs, and git:// URLs. // function stripCredentials(host) { const at = host.lastIndexOf("@"); return at === -1 ? host : host.slice(at + 1); } function splitPath(path) { const parts = path .replace(/^\/+/, "") .replace(/\.git$/i, "") .replace(/\/+$/, "") .split("/") .filter(Boolean); if (parts.length < 2) return null; // Sub-path installs ("https://example.com/gitea/owner/repo") still end with // owner/repo, so take the last two components. return { owner: parts[parts.length - 2], name: parts[parts.length - 1] }; } /** Parses a remote URL into { host, port, owner, name }, or null. */ function parse(url) { if (!url) return null; const trimmed = String(url).trim(); let match = trimmed.match(/^[a-z][a-z0-9+.-]*:\/\/([^/]+)(\/.*)$/i); if (match) { let authority = stripCredentials(match[1]); let port = null; const portMatch = authority.match(/^(.+):(\d+)$/); if (portMatch) { authority = portMatch[1]; port = Number(portMatch[2]); } const path = splitPath(match[2]); if (!path) return null; return { host: authority.toLowerCase(), port: port, owner: path.owner, name: path.name }; } // SCP-like: git@host:owner/repo.git match = trimmed.match(/^([^/@]+@)?([^/:]+):(.+)$/); if (match) { const path = splitPath(match[3]); if (!path) return null; return { host: match[2].toLowerCase(), port: null, owner: path.owner, name: path.name, }; } return null; } /** * The bare host of an instance base URL, a remote URL, or a plain hostname, * without scheme, credentials, port, or path. Used to compare a remote against * a configured instance and to normalise both sides of a host alias. */ function hostOf(value) { const text = String(value || "").trim(); if (!text) return null; const scheme = text.match(/^[a-z][a-z0-9+.-]*:\/\/([^/]+)/i); // Without a scheme this is already a bare host, possibly carrying // credentials, a port, or an SCP-style path after a colon. const authority = scheme ? scheme[1] : text.replace(/[/:].*$/, ""); const host = stripCredentials(authority).replace(/:\d+$/, "").toLowerCase(); return host || null; } exports.parse = parse; exports.hostOf = hostOf;