From 8ab393d8d252c100c24f36c7b1657f936c54752f Mon Sep 17 00:00:00 2001 From: Silberengel Date: Mon, 23 Feb 2026 13:53:25 +0100 Subject: [PATCH] Sync from gitrepublic-web monorepo - 2026-02-23 13:53:25 --- scripts/commands/pullAll.js | 84 +++++++++++++++++++++++++++++++------ scripts/git-wrapper.js | 1 + 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/scripts/commands/pullAll.js b/scripts/commands/pullAll.js index 8e9d6a9..3cebcef 100644 --- a/scripts/commands/pullAll.js +++ b/scripts/commands/pullAll.js @@ -17,33 +17,88 @@ async function getRemoteUrl(remote) { }); } +/** + * Check if a URL is an SSH URL (git@host:path or ssh://) + */ +function isSshUrl(url) { + return url.startsWith('git@') || url.startsWith('ssh://') || /^[a-zA-Z0-9_]+@/.test(url); +} + +/** + * Convert SSH URL to HTTPS URL for reachability testing + * Examples: + * git@github.com:user/repo.git -> https://github.com/user/repo.git + * git@git.imwald.eu:2222/user/repo.git -> https://git.imwald.eu/user/repo.git + * ssh://git@host:port/path -> https://host/path + */ +function sshToHttps(url) { + // Handle ssh:// URLs + if (url.startsWith('ssh://')) { + const match = url.match(/^ssh:\/\/(?:[^@]+@)?([^:\/]+)(?::(\d+))?(?:\/(.+))?$/); + if (match) { + const [, host, port, path] = match; + const cleanPath = path || ''; + // Remove port from HTTPS URL (ports are usually SSH-specific) + return `https://${host}${cleanPath.startsWith('/') ? cleanPath : '/' + cleanPath}`; + } + } + + // Handle git@host:path format + if (url.startsWith('git@') || /^[a-zA-Z0-9_]+@/.test(url)) { + const match = url.match(/^(?:[^@]+@)?([^:]+):(.+)$/); + if (match) { + const [, host, path] = match; + // Remove port if present (e.g., git.imwald.eu:2222 -> git.imwald.eu) + const hostWithoutPort = host.split(':')[0]; + const cleanPath = path.startsWith('/') ? path : '/' + path; + return `https://${hostWithoutPort}${cleanPath}`; + } + } + + return null; +} + /** * Check if a git URL is reachable * Tests the info/refs endpoint to see if the server responds + * Converts SSH URLs to HTTPS for testing */ async function checkUrlReachability(url, timeout = 5000) { - try { - // Handle SSH URLs (git@host:path or ssh://git@host/path) - if (url.startsWith('git@') || url.startsWith('ssh://')) { - // For SSH URLs, we can't easily test reachability via HTTP - // Assume reachable (user has SSH access configured) + let testUrl = url; + + // Convert SSH URLs to HTTPS for testing + if (isSshUrl(url)) { + const httpsUrl = sshToHttps(url); + if (httpsUrl) { + testUrl = httpsUrl; + } else { + // If we can't convert, assume reachable (will fail on actual fetch if not) return { reachable: true, error: undefined }; } - + } + + try { // Parse URL and construct test endpoint - let testUrl = url; + let finalTestUrl = testUrl; // Handle git:// URLs - if (url.startsWith('git://')) { - testUrl = url.replace('git://', 'http://'); + if (finalTestUrl.startsWith('git://')) { + finalTestUrl = finalTestUrl.replace('git://', 'http://'); } // Ensure URL ends with .git for the test - if (!testUrl.endsWith('.git')) { - testUrl = testUrl.replace(/\/$/, '') + '.git'; + if (!finalTestUrl.endsWith('.git')) { + finalTestUrl = finalTestUrl.replace(/\/$/, '') + '.git'; + } + + const urlObj = new URL(finalTestUrl); + + // Only test HTTP/HTTPS URLs + if (urlObj.protocol !== 'http:' && urlObj.protocol !== 'https:') { + // For other protocols, assume reachable + return { reachable: true, error: undefined }; } - const urlObj = new URL(testUrl); const testEndpoint = `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}/info/refs?service=git-upload-pack`; const controller = new AbortController(); @@ -70,6 +125,11 @@ async function checkUrlReachability(url, timeout = 5000) { return { reachable: false, error: fetchError instanceof Error ? fetchError.message : 'Network error' }; } } catch (urlError) { + // If URL parsing fails, it might be a malformed URL + // For SSH URLs that we couldn't convert, assume reachable (will fail on actual fetch if not) + if (isSshUrl(url)) { + return { reachable: true, error: undefined }; + } return { reachable: false, error: urlError instanceof Error ? urlError.message : 'Invalid URL' }; } } diff --git a/scripts/git-wrapper.js b/scripts/git-wrapper.js index 3ebe335..ec70892 100755 --- a/scripts/git-wrapper.js +++ b/scripts/git-wrapper.js @@ -42,6 +42,7 @@ const REMOTE_COMMANDS = ['clone', 'push', 'pull', 'fetch', 'ls-remote']; // API commands that should be handled by gitrepublic.js const API_COMMANDS = [ 'push-all', 'pushAll', + 'pull-all', 'pullAll', 'repos', 'repo', 'file', 'search',