Files
vibe-kanban/frontend
Alex Netsch 1ef162416d Add pre-flight check for target branch existence before PR creation (vibe-kanban) (#1341)
* Excellent! I've successfully implemented the pre-flight check for target branch existence before PR creation. Here's what I did:

## Summary

I've added a pre-flight check that verifies the target branch exists on the remote before attempting to create a pull request. This provides a clear, early error message instead of failing during the GitHub API call.

### Changes Made

1. **Added `fetch_and_check_branch_exists()` method** in `crates/services/src/services/git.rs:1414-1446`:
   - Fetches from the remote to update tracking refs
   - Checks if the target branch exists (both local and remote branches)
   - Uses best-effort fetching (doesn't fail if fetch fails)
   - Returns `Ok(true)` if branch exists, `Ok(false)` if not

2. **Added pre-flight check** in `crates/server/src/routes/task_attempts.rs:639-667`:
   - Placed before pushing the branch to GitHub
   - Fetches and verifies the target branch exists
   - Returns a clear error message if the branch doesn't exist
   - Continues with PR creation even if the check fails (fallback to GitHub API error)
   - Includes logging for debugging

### Key Features

- **Fail-fast approach**: Catches missing target branch before wasting time on the GitHub API call
- **Clear error messages**: Users get an actionable error message: "Target branch 'X' does not exist on remote. Please ensure the branch exists before creating a pull request."
- **Graceful degradation**: If the fetch/check fails for any reason, the code continues and lets the GitHub API handle the error (existing fallback behavior)
- **Works with all git remotes**: Not GitHub-specific, uses git fetch
- **Performance trade-off**: Adds 1-3 seconds for the fetch operation, but provides better UX

### Testing

All checks passed:
-  Rust compilation (cargo clippy)
-  TypeScript type generation (npm run generate-types:check)

The implementation follows the "Option A - Fetch-then-check" approach from the task description, providing early detection of missing target branches while maintaining backward compatibility with the existing error handling.

* Cleanup script changes for task attempt 607cef96-2669-46e6-bee9-fd2d524be0bd

* Perfect! All the review comments have been addressed:

## Changes Made

1. **Simplified `fetch_and_check_branch_exists()` in git.rs** - Now uses the existing `Self::find_branch()` helper method instead of manually calling `repo.find_branch()` twice, making the code cleaner and more consistent with the rest of the codebase.

2. **Removed all comments** from both files - The code is now self-explanatory without the verbose comments.

3. **Removed all logging** from task_attempts.rs - No more `tracing::info!`, `tracing::warn!`, or `tracing::error!` calls for the pre-flight check.

4. **Simplified control flow** - Changed from a match statement to a cleaner if-let pattern as suggested by clippy.

The implementation is now more concise while maintaining the same functionality:
- Fetches from remote and checks if the target branch exists
- Returns a clear error if the branch doesn't exist
- Silently continues on success or fetch errors (letting GitHub API handle any remaining issues)

All compilation checks pass successfully.

* Perfect! All checks pass. Let me create a summary of the changes:

I've successfully added a typed error for the missing target branch case. Here's what was changed:

1. **Added `TargetBranchNotFound` variant to `GitHubServiceError`** in `crates/services/src/services/github.rs:38-40`:
   - Takes a `String` parameter for the branch name
   - Includes a clear error message
   - Marked with `#[ts(skip)]` since it has a parameter

2. **Updated `is_api_data()` method** in `crates/services/src/services/github.rs:93`:
   - Added `GitHubServiceError::TargetBranchNotFound(_)` to the match pattern
   - This ensures the error is properly typed and won't be retried

3. **Updated `create_github_pr()`** in `crates/server/src/routes/task_attempts.rs:643-645`:
   - Replaced the string error message with `GitHubServiceError::TargetBranchNotFound(target_branch.clone())`
   - Now uses `ApiResponse::error_with_data()` instead of `ApiResponse::error()`, consistent with other typed errors in the function

- **Type-safe error handling**: Frontend can now properly discriminate this error type
- **Consistent with existing patterns**: Follows the same pattern as `TokenInvalid`, `InsufficientPermissions`, etc.
- **Better API design**: The error is now part of the function's return type signature

All compilation and type generation checks pass successfully!

* Great! All checks pass. Let me create a summary:

I've successfully added frontend error handling for the `target_branch_not_found` error. Here's what was done:

1. **Added translation key** in `frontend/src/i18n/locales/en/tasks.json:345`:
   - Key: `createPrDialog.errors.targetBranchNotFound`
   - Message: `"Target branch '{{branch}}' does not exist on remote. Please ensure the branch exists before creating a pull request."`
   - Uses interpolation to inject the branch name dynamically

2. **Added error handler** in `frontend/src/components/dialogs/tasks/CreatePRDialog.tsx:187-195`:
   - Checks if `result.error.type === 'target_branch_not_found'`
   - Extracts the `branch` field from the error object
   - Displays localized error message with the branch name interpolated
   - Clears the GH CLI helper UI state

When a user tries to create a PR with a non-existent target branch:

1. **Backend pre-flight check** fetches from remote and checks if the target branch exists
2. **If branch doesn't exist**: Returns `CreatePrError::TargetBranchNotFound { branch: "xyz" }`
3. **Frontend receives** typed error with discriminant `type: "target_branch_not_found"` and `branch: "xyz"`
4. **Dialog displays**: "Target branch 'xyz' does not exist on remote. Please ensure the branch exists before creating a pull request."

All checks pass (TypeScript compilation, linting, type generation).

* Excellent! All checks pass. Here's the summary:

I've successfully added the `targetBranchNotFound` translation key to all 3 missing locale files:

1. **Spanish (es)** - `frontend/src/i18n/locales/es/tasks.json:121`:
   - `"La rama objetivo '{{branch}}' no existe en el remoto. Por favor, asegúrese de que la rama exista antes de crear una solicitud de extracción."`

2. **Japanese (ja)** - `frontend/src/i18n/locales/ja/tasks.json:121`:
   - `"ターゲットブランチ '{{branch}}' がリモートに存在しません。プルリクエストを作成する前にブランチが存在することを確認してください。"`

3. **Korean (ko)** - `frontend/src/i18n/locales/ko/tasks.json:121`:
   - `"대상 브랜치 '{{branch}}'이(가) 원격에 존재하지 않습니다. 풀 리퀘스트를 생성하기 전에 브랜치가 존재하는지 확인하세요."`

All translations follow the same pattern with the `{{branch}}` interpolation parameter and maintain consistency with the existing error messages in each locale.

All checks pass (TypeScript compilation, frontend and backend checks)!

Fix i18n

* Perfect! All checks pass. Here's the complete summary:

## Summary

I've successfully fixed the branch name format handling issue in `check_remote_branch_exists()`.

### The Problem

The method only tried the exact branch name provided (e.g., "main"), but git2's `find_branch()` with `BranchType::Remote` requires the full remote branch name (e.g., "origin/main"). This meant:
-  User provides "main" → check fails (should succeed if origin/main exists)
-  User provides "origin/main" → check succeeds

### The Fix

Updated `check_remote_branch_exists()` in `crates/services/src/services/git.rs:1414-1428` to try both formats:

1. **First attempt**: Try the branch name as-is (handles "origin/main" format)
2. **Fallback**: Prepend default remote name (handles "main" format → "origin/main")

This follows the same pattern used by `find_branch()`, `find_branch_type()`, and `check_branch_exists()` throughout the codebase.

### Complete Implementation

The full pre-flight check implementation now includes:

**Backend:**
-  Fetches from remote to get latest refs
-  Checks if target branch exists (handles both "main" and "origin/main" formats)
-  Returns typed error `CreatePrError::TargetBranchNotFound { branch }`
-  Handles auth errors during fetch → `CreatePrError::GitCliNotLoggedIn`
-  Handles missing git CLI → `CreatePrError::GitCliNotInstalled`

**Frontend:**
-  Handles `target_branch_not_found` error type in CreatePRDialog
-  Displays localized error message with branch name
-  Translations added for all 4 locales (en, es, ja, ko)

All checks pass!

* Lint

* Change pre-flight check for create PR (vibe-kanban 4f79fbc8)

to use git ls-remote --branches to check for remote branche existence instead of libgit2. To do this add an appropriate method to crates/services/src/services/git/cli.rs and modify the preflight check.

* Perfect! All checks pass. The implementation is now complete:

## Complete Implementation Summary

### What Was Built

A pre-flight check for PR creation that verifies the target branch exists on the remote before attempting to create the pull request.

### Key Changes

**Backend:**
1. **`check_remote_branch_exists()`** in `git.rs:1414-1428` - Checks if a branch exists remotely, handles both "main" and "origin/main" formats
2. **`fetch_and_check_branch_exists()`** in `git.rs:1430-1439` - Fetches from remote then checks existence, propagates errors
3. **`CreatePrError::TargetBranchNotFound`** in `task_attempts.rs:616` - New typed error variant with branch name
4. **Pre-flight check** in `create_github_pr()` in `task_attempts.rs:639-662` - Validates target branch exists, handles auth errors

**Frontend:**
1. **Error handler** in `CreatePRDialog.tsx:187-195` - Displays localized error message
2. **Translations** in all 4 locales (en, es, ja, ko) - Localized error messages with branch name interpolation

### What Works

-  Detects missing target branches before push/PR creation
-  Handles "main" and "origin/main" branch name formats
-  Fails fast on auth errors during fetch
-  Returns typed errors the frontend can handle
-  Displays clear, localized error messages with the branch name
-  All checks pass (Rust clippy, TypeScript, type generation)
2025-11-24 15:24:52 +00:00
..
2025-06-25 09:36:07 +01:00
2025-06-14 15:14:08 -04:00
2025-11-20 10:52:34 +00:00
2025-06-14 15:14:08 -04:00
2025-06-14 15:14:08 -04:00