From 50454175c98b03afc6c2c154ed272bb0819130e4 Mon Sep 17 00:00:00 2001 From: simos Date: Fri, 31 Oct 2025 08:59:02 +0000 Subject: [PATCH] fix(agent): improve branch name and URL parsing Enhance the robustness of GitHub URL parsing and branch name generation with better regex patterns and edge case handling. Changes: - Update GitHub URL regex to use non-greedy matching and anchored .git suffix detection for more precise parsing - Replace string-based .git removal with regex-based end anchor - Add comprehensive validation for empty branch names with fallback - Implement proper length calculation accounting for timestamp suffix - Add final regex validation to ensure branch names meet safety requirements - Improve edge case handling for hyphens after truncation - Add deterministic fallback for invalid branch name patterns These changes prevent potential parsing errors with malformed URLs and ensure generated branch names always meet Git naming conventions. --- server/routes/agent.js | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/server/routes/agent.js b/server/routes/agent.js index cb40f9a..71ad1f6 100644 --- a/server/routes/agent.js +++ b/server/routes/agent.js @@ -90,13 +90,13 @@ function normalizeGitHubUrl(url) { function parseGitHubUrl(url) { // Handle HTTPS URLs: https://github.com/owner/repo or https://github.com/owner/repo.git // Handle SSH URLs: git@github.com:owner/repo or git@github.com:owner/repo.git - const match = url.match(/github\.com[:/]([^/]+)\/([^/.]+)/); + const match = url.match(/github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?$/); if (!match) { throw new Error('Invalid GitHub URL format'); } return { owner: match[1], - repo: match[2].replace('.git', '') + repo: match[2].replace(/\.git$/, '') }; } @@ -114,14 +114,37 @@ function autogenerateBranchName(message) { .replace(/-+/g, '-') // Replace multiple hyphens with single .replace(/^-|-$/g, ''); // Remove leading/trailing hyphens - // Limit length to 50 characters - if (branchName.length > 50) { - branchName = branchName.substring(0, 50).replace(/-$/, ''); + // Ensure non-empty fallback + if (!branchName) { + branchName = 'task'; } - // Add timestamp suffix to ensure uniqueness - const timestamp = Date.now().toString(36).substring(-6); - branchName = `${branchName}-${timestamp}`; + // Generate timestamp suffix (last 6 chars of base36 timestamp) + const timestamp = Date.now().toString(36).slice(-6); + const suffix = `-${timestamp}`; + + // Limit length to ensure total length including suffix fits within 50 characters + const maxBaseLength = 50 - suffix.length; + if (branchName.length > maxBaseLength) { + branchName = branchName.substring(0, maxBaseLength); + } + + // Remove any trailing hyphen after truncation and ensure no leading hyphen + branchName = branchName.replace(/-$/, '').replace(/^-+/, ''); + + // If still empty or starts with hyphen after cleanup, use fallback + if (!branchName || branchName.startsWith('-')) { + branchName = 'task'; + } + + // Combine base name with timestamp suffix + branchName = `${branchName}${suffix}`; + + // Final validation: ensure it matches safe pattern + if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(branchName)) { + // Fallback to deterministic safe name + return `branch-${timestamp}`; + } return branchName; }