Add Azure DevOps repository support for PR operations (Vibe Kanban) (#1808)
* All tasks are complete. Let me summarize what was implemented:
I've successfully implemented Azure DevOps repos support alongside the existing GitHub integration. Here's what was done:
- **`mod.rs`** - Defines the `GitHostService` trait and factory functions for creating services
- **`types.rs`** - Unified types: `GitHostProvider`, `RepoInfo`, `GitHostError`, `CreatePrRequest`, `UnifiedPrComment`
- **`detection.rs`** - URL pattern matching for auto-detecting GitHub (including Enterprise) and Azure DevOps
- **`github/mod.rs`** - `GitHubHostService` implementing the trait
- **`github/cli.rs`** - `GhCli` wrapper (moved from old location)
- **`azure/mod.rs`** - `AzureHostService` implementing the trait
- **`azure/cli.rs`** - `AzCli` wrapper for Azure DevOps CLI commands
1. **Auto-detection**: The system detects the provider from the git remote URL:
- `github.com` → GitHub
- `github.*.com` → GitHub Enterprise
- `dev.azure.com`, `*.visualstudio.com`, or URLs with `/_git/` → Azure DevOps
2. **Unified trait interface**: Both providers implement `GitHostService` with methods for:
- `get_repo_info()` - Get repository identification
- `check_auth()` - Verify CLI authentication
- `create_pr()` - Create pull requests
- `get_pr_status()` - Get PR status by URL
- `list_prs_for_branch()` - List PRs for a branch
- `get_pr_comments()` - Get PR comments
3. **Azure CLI commands supported**:
- `az repos pr create` - Create PRs
- `az repos pr show` - View PR details
- `az repos pr list` - List PRs
- `az repos pr list-threads` - Get PR comments
- `az account show` - Auth check
- `pr.rs` - Uses auto-detection for PR creation
- `pr_monitor.rs` - Uses provider detection from PR URL
- `task_attempts.rs` - Uses git_host for push auth checking
- `error.rs` - Updated `GitHost` error variant
- `share.rs` - Updated `GitHost` error variant
- `generate_types.rs` - Updated type exports
- Old `crates/services/src/services/github.rs` module and directory (replaced by git_host)
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
All tests pass, including the new URL decoding tests. The fix is complete - the URL-encoded components (like `Vibe%20Kanban%20Test`) will now be properly decoded to `Vibe Kanban Test` before being passed to the Azure CLI.
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
All done! Here's a summary of the changes:
Updated `get_repo_info()` to use the Azure CLI's `az repos show --detect true` command instead of manual URL parsing, similar to how GitHub uses `gh repo view --json owner,name`.
1. **Added `extract_repo_name_from_url()`** - A simple helper that extracts just the repository name from Azure DevOps URLs (looks for `/_git/{repo}` pattern). This is still needed because `az repos show` requires the `--repository` parameter.
2. **Updated `get_repo_info()`** - Now uses `az repos show --repository {name} --detect true --output json` which:
- Auto-detects organization and project from git config
- Returns canonical names (properly decoded)
- Validates the repo exists
3. **Added `extract_org_from_response()`** - Extracts the organization from the `remoteUrl` field in the CLI response, with fallback to URL parsing.
4. **Fixed `run()` method** - Now properly passes the `dir` parameter to `Command::current_dir()` so the `--detect` flag works correctly.
5. **Kept `parse_azure_remote_url()`** - Still used as a fallback for organization extraction and for PR URL parsing.
6. **Added tests** - New tests for `extract_repo_name_from_url()` covering various URL formats.
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
Done! The cleanup is complete. Here's what was removed:
Removed the brittle `parse_azure_remote_url()` function and related code:
1. **Removed `AzureRepoInfo` struct** - No longer needed
2. **Removed `parse_azure_remote_url()` function** - The ~90-line function that manually parsed various Azure DevOps URL formats
3. **Updated `get_repo_info()`** - Changed from fallback (`unwrap_or_else`) to error (`ok_or_else`) when organization can't be extracted from the CLI response
4. **Removed 4 tests** - `test_parse_azure_remote_url_https`, `test_parse_azure_remote_url_visualstudio`, `test_parse_azure_remote_url_ssh`, `test_parse_azure_remote_url_with_spaces`
The code now relies entirely on:
- `extract_repo_name_from_url()` - Simple helper to get repo name from URL (for passing to `az repos show`)
- `az repos show --detect true` - Azure CLI command that auto-detects org/project from git config
- `extract_org_from_response()` - Extracts org from the CLI's `remoteUrl` JSON field
The implementation is complete. Here's a summary of the changes:
Simplified the Azure DevOps integration by using the CLI's `--detect` flag instead of parsing URLs to extract org/project/repo.
1. **`types.rs`** - Changed `RepoInfo::AzureDevOps` to store just `repo_path: PathBuf` instead of `organization`, `project`, and `repo_name`. Removed the unused `repo_name()` method.
2. **`cli.rs`** - Major simplification:
- Removed URL parsing helpers: `decode_url_component()`, `extract_repo_name_from_url()`, `extract_org_from_response()`
- `get_repo_info()` now just verifies it's a valid Azure repo via `az repos show --detect true` and returns the path
- `create_pr()` now takes `repo_path` and uses `--detect true` instead of explicit org/project/repo
- `list_prs_for_branch()` now takes `repo_path` and uses `--detect true`
- `get_pr_threads()` now takes `repo_path` and uses `--detect true`
- `view_pr()` now takes the full PR URL and parses org + PR ID internally
- `parse_pr_url()` simplified to return just `(org, pr_id)` instead of 4 values
- Parsing functions simplified to extract URLs from response's `repository.webUrl` field
- Updated tests for the new API
3. **`mod.rs`** - Updated `AzureHostService` to:
- Extract `repo_path` from `RepoInfo::AzureDevOps`
- Pass `repo_path` to CLI methods that need it
- Pass the PR URL directly to `view_pr()` for `get_pr_status()`
- URL parsing reduced to minimal extraction of org + PR ID from PR URLs (only needed for `get_pr_status()`)
- Repo-based operations don't parse URLs at all - CLI auto-detects via git config
- Much cleaner, less brittle code
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
All done. The implementation is complete:
**Summary of changes:**
1. **`types.rs`**: Updated `RepoInfo::AzureDevOps` to store `organization_url`, `project`, `repo_name` instead of `repo_path`
2. **`cli.rs`**:
- `get_repo_info()` now parses the JSON response from `az repos show --detect true` to extract repo name, project name, and organization URL
- Added `extract_org_url()` helper to extract the base org URL from the API URL
- Updated `create_pr()`, `list_prs_for_branch()`, `get_pr_threads()` to take explicit `organization_url`, `project`, `repo_name` params
3. **`mod.rs`** (`AzureHostService`):
- `create_pr()`, `list_prs_for_branch()`, `get_pr_comments()` all now extract org/project/repo from `RepoInfo` and pass them to the CLI methods
The key insight was that the `url` field from `az repos show` (e.g., `https://dev.azure.com/{org}/.../_apis/...`) can be truncated to `https://dev.azure.com/{org}` and used directly with the `--organization` flag — minimal URL parsing needed.
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
* Done. Refactored all 4 `serde_json::Value` parsing sites to use typed structs:
**Changes to `cli.rs`:**
1. **Added typed response structs** with `#[derive(Deserialize)]`:
- `AzRepoShowResponse`, `AzProject` - for `az repos show`
- `AzPrResponse`, `AzRepository`, `AzCommit` - for PR responses
- `AzThread`, `AzThreadContext`, `AzFilePosition`, `AzThreadComment`, `AzAuthor` - for thread comments
2. **Updated parsing functions**:
- `get_repo_info()` - now deserializes into `AzRepoShowResponse`
- `parse_pr_response()` - now deserializes into `AzPrResponse`
- `parse_pr_list_response()` - now deserializes into `Vec<AzPrResponse>`
- `parse_pr_threads()` - now deserializes into `Vec<AzThread>`
3. **Replaced `extract_pr_info()`** with `az_pr_to_info()` that takes the typed struct directly
4. **Removed `serde_json::Value` import** - no longer needed
The code is now cleaner with no `.get().and_then().as_str()` chains, and serde provides better error messages on parse failures.
* Done. Added the missing `--repository` parameter to `get_pr_threads()`:
1. **`cli.rs`**: Added `repo_name: &str` parameter and `--repository` flag to the CLI command
2. **`mod.rs`**: Updated `get_pr_comments()` to extract both `organization_url` and `repo_name` from `RepoInfo` and pass them to the CLI
Done. Here's a summary of the changes:
**Changes made to `crates/services/src/services/git_host/azure/cli.rs`:**
1. **Removed git2 dependency** - No longer imports `git2::Repository`
2. **Added new typed structs** for `az repos list` response:
- `AzRepoListItem` - with `name`, `project`, and `remote_url` fields
- `AzRepoProject` - with `name` field
3. **Rewrote `get_repo_info()`** to use `az repos list --detect true`:
- Runs CLI command from the repo directory to auto-detect org/project
- Takes the first repo from the list (assuming single repo per directory)
- Extracts org from `remoteUrl` field
4. **Replaced `parse_azure_remote_url()`** with simpler `extract_org_from_remote_url()`:
- Only extracts the organization name from the URL
- Handles both `dev.azure.com` and `visualstudio.com` formats
- Much simpler than the previous 3-tuple URL parser
5. **Updated tests** - Replaced the old URL parsing tests with new `extract_org_from_remote_url` tests
The key improvement is that `az repos list --detect true` uses the Azure CLI's built-in detection (from git config) to find the org and project, then returns all repo info including project name. This avoids git2 library usage and complex URL parsing entirely.
Done. Here's a summary of all changes:
**Summary of Changes**
1. **`crates/services/src/services/git_host/types.rs`**:
- Added `project_id` and `repo_id` fields to `RepoInfo::AzureDevOps` enum variant
2. **`crates/services/src/services/git_host/azure/cli.rs`**:
- Added `id` field to `AzRepoListItem` struct
- Added `id` field to `AzRepoProject` struct
- Added `AzThreadsResponse` struct to wrap the `{ "value": [...] }` REST API response
- Updated `get_repo_info()` to return `project_id` and `repo_id` from `az repos list` output
- Updated `get_pr_threads()` to:
- Accept `project_id` and `repo_id` instead of names
- Use `az rest` with REST API URL instead of non-existent `az repos pr list-threads`
- Updated `parse_pr_threads()` to unwrap the `value` field from REST API response
3. **`crates/services/src/services/git_host/azure/mod.rs`**:
- Updated `create_pr` and `list_prs_for_branch` to use `..` pattern for new fields
- Updated `get_pr_comments()` to extract and pass `project_id` and `repo_id` to CLI
**Key Improvements**:
- Uses UUIDs (`project_id`, `repo_id`) instead of names for REST API calls, avoiding URL encoding issues with names containing spaces like "Vibe Kanban Test"
- Fixed `get_pr_threads()` to use `az rest` since `az repos pr list-threads` doesn't exist as an Azure CLI command
- All unit tests pass
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
Done. The fix adds `--resource 499b84ac-1321-427f-aa17-267ca6975798` to the `az rest` command, which tells it to request an Azure DevOps token instead of a generic Azure token. This should now properly authenticate to the Azure DevOps REST API.
Done. The fix replaces `az rest` with `az devops invoke --area git --resource pullRequestThreads`. This uses the same authentication as other `az repos pr` commands, so it should work without requiring the separate "identity materialization" step.
Done. The fix replaces the single combined string for `--route-parameters` with individual arguments for each `key=value` pair. This matches how the Azure CLI expects the parameters to be passed - each route parameter should be a separate shell argument after the `--route-parameters` flag.
Done. Removed all the requested comments from:
- `crates/server/src/routes/task_attempts.rs` (lines 451, 490)
- `crates/server/src/routes/task_attempts/pr.rs` (lines 283, 516)
- `crates/services/src/services/git_host/azure/cli.rs` (section header comment, doc comments on structs and methods)
* Done. Removed the module-level doc comment and all the method doc comments from the `GitHostService` trait and public functions in `mod.rs`.
Done. Removed the obvious/redundant comments from `types.rs`.
Done. Removed the unnecessary `Arc` usage from all three functions in `mod.rs`. The strings are already cloned when extracted from `RepoInfo`, so we can just clone them directly in the retry closure.
* All tests pass. Fixed the `npm run check` and `npm run lint` issues by:
1. Regenerated TypeScript types from Rust
2. Updated `frontend/src/lib/api.ts` to use `CreatePrApiRequest` instead of `CreateGitHubPrRequest`
3. Updated `CreatePRDialog.tsx` to handle the new error types (`cli_not_installed`/`cli_not_logged_in` with a `provider` field instead of `github_cli_not_installed`/`github_cli_not_logged_in`)
4. Fixed clippy warnings about collapsible if statements in `parse_pr_url()`
* Done. I've made `url`, `diff_hunk`, and `author_association` optional in the `UnifiedPrComment` type:
**Changes made:**
1. **`crates/services/src/services/git_host/types.rs`** - Changed the `UnifiedPrComment` enum:
- `url: String` → `url: Option<String>`
- `diff_hunk: String` → `diff_hunk: Option<String>`
- `author_association: String` → `author_association: Option<String>`
2. **`crates/services/src/services/git_host/azure/cli.rs`** - Updated Azure comment creation to use `None` instead of empty strings
3. **`crates/services/src/services/git_host/github/mod.rs`** - Wrapped GitHub values in `Some()`
4. **`frontend/src/components/ui/github-comment-card.tsx`** - Updated interface to allow `url?: string | null` and `diffHunk?: string | null`
5. **`frontend/src/components/ui/wysiwyg/nodes/github-comment-node.tsx`**:
- Updated `NormalizedComment` interface
- Fixed click handler to check `if (data.url)` before opening
- Removed `data.url` from validation (only `id`, `comment_type`, `author`, `body` required)
All checks pass (`pnpm run check`, `pnpm run lint`, `cargo test --workspace`).
* Done. All three changes are complete:
1. **Removed unnecessary temp file** in `azure/cli.rs:create_pr()` - was writing body to temp file then immediately reading it back. Now just passes `body` directly to `OsString`.
2. **Added structs for JSON parsing** in `github/cli.rs:parse_pr_comments()` - replaced manual `item.get("field")...` parsing with `GhCommentsWrapper` and `GhCommentResponse` structs using serde.
3. **Added structs for JSON parsing** in `github/cli.rs:parse_pr_review_comments()` - replaced manual parsing with `GhReviewCommentResponse` struct.
All checks pass (`pnpm run check`, `pnpm run lint`, `cargo test --workspace`).
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
Done. Both changes are complete:
1. **Inlined single-field structs** - Consolidated `GhCommentAuthor` and `GhReviewCommentUser` into a single `GhUserLogin` struct that's reused by both `GhCommentResponse` and `GhReviewCommentResponse`.
2. **Converted `extract_pr_info` to struct parsing** - Added `GhPrResponse` and `GhMergeCommit` structs. Replaced the manual `Value::get()` parsing with serde deserialization. The new `pr_response_to_info()` helper converts the parsed struct to `PullRequestInfo`. Also removed the now-unused `serde_json::Value` import.
All checks pass (`pnpm run check`, `pnpm run lint`, `cargo test --workspace`).
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
* Done. I've updated `detection.rs` to use `GitCli` instead of `git2`:
1. **Added `get_remote_url` method to `GitCli`** (`git/cli.rs`) - Uses `git remote get-url <remote>` to get the URL
2. **Updated `detect_provider`** (`git_host/detection.rs`) - Now uses `GitCli::get_remote_url()` instead of `git2::Repository`
This keeps the codebase consistent with the pattern of using `GitCli` for git operations rather than mixing `git2` directly in various modules.
All checks pass (`pnpm run check`, `pnpm run lint`, `cargo test --workspace`).
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
* Done! The refactor is complete. Here's a summary of the changes:
**New Architecture:**
1. **`ProviderKind` enum** (was `GitHostProvider`) - The discriminant for provider types (GitHub, AzureDevOps, Unknown)
2. **`GitHostProvider` trait** (was `GitHostService`) - The interface that provider implementations must satisfy
3. **`GitHubProvider` struct** (was `GitHubHostService`) - GitHub implementation
4. **`AzureDevOpsProvider` struct** (was `AzureHostService`) - Azure DevOps implementation
5. **`GitHostService` struct** (new) - Main entry point that:
- Holds repo context (`repo_path`, `GitCli`)
- Provides `provider_for_remote(remote_name)` to get a provider for a specific remote
- Provides `provider_from_url(url)` for URL-based detection (e.g., PR URLs)
- Provides `provider_for_kind(kind)` for direct provider construction
**Benefits:**
- Provider detection is now contextual - callers specify which remote they want
- The service struct encapsulates provider detection logic
- Cleaner separation: `GitHostService` is the facade, `GitHostProvider` trait is the contract
- Ready for multi-remote support (different providers for different remotes)
All checks pass (`pnpm run check`, `pnpm run lint`, `cargo test --workspace`).
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
All the call sites have been updated to use the new cleaner `GitHostService` facade API. Here's a summary of the changes:
**Changes made:**
1. **`pr.rs:414-418` (`attach_existing_pr`)**: Simplified from:
```rust
let git_host = git_host::GitHostService::new(&repo.path);
let git_host_provider = git_host.provider_for_remote("origin")?;
let repo_info = git_host_provider.get_repo_info(&repo.path).await?;
let prs = git_host_provider.list_prs_for_branch(&repo_info, &workspace.branch).await?;
```
To:
```rust
let git_host = git_host::GitHostService::new(&repo.path);
let prs = git_host.list_prs_for_branch(&workspace.branch).await?;
```
2. **`pr.rs:511-525` (`get_pr_comments`)**: Simplified from provider-based pattern to:
```rust
let git_host = git_host::GitHostService::new(&repo.path);
let provider = git_host.provider_kind()?;
match git_host.get_pr_comments(pr_info.number).await { ... }
```
3. **`task_attempts.rs:451-453` (`push_task_attempt_branch`)**: Removed explicit `check_auth()` call since auth is now checked during the actual operation. Just verify provider is supported:
```rust
let git_host = git_host::GitHostService::new(&worktree_path);
git_host.provider_kind()?;
```
4. **`task_attempts.rs:490-492` (`force_push_task_attempt_branch`)**: Same simplification.
The new API is much cleaner - call sites just create a `GitHostService::new(path)` and call methods directly without dealing with providers, repo info, or explicit auth checks.
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
The refactoring is complete. Here's a summary of what was done:
**Changes made:**
1. **`crates/services/Cargo.toml`** - Added `enum_dispatch` dependency
2. **`crates/services/src/services/git_host/mod.rs`** - Completely rewritten:
- `GitHostProvider` is now a public trait with `#[enum_dispatch(GitHostService)]`
- `GitHostService` is now an enum with `GitHub` and `AzureDevOps` variants
- Constructors: `from_pr_url(url)` and `from_remote_url(url)` - no `new()` method
- Methods now take `repo_path` as a parameter where needed
3. **`crates/services/src/services/git_host/github/mod.rs`** - Updated to match new trait signature (methods take `repo_path`, `check_auth` called inside `create_pr`)
4. **`crates/services/src/services/git_host/azure/mod.rs`** - Updated similarly
5. **`crates/services/src/services/git.rs`** - Added `get_remote_url()` method to `GitService`
6. **Call site updates:**
- `pr_monitor.rs` - Uses `from_pr_url`
- `pr.rs` - Uses `get_remote_name_from_branch_name` + `get_remote_url` + `from_remote_url`
- `task_attempts.rs` - Same pattern for push functions
**New API usage:**
```rust
// From a PR URL (e.g., for checking PR status)
let git_host = GitHostService::from_pr_url(&pr_url)?;
let status = git_host.get_pr_status(&pr_url).await?;
// From a remote URL (e.g., for creating PRs)
let remote_name = git_service.get_remote_name_from_branch_name(&repo_path, &branch)?;
let remote_url = git_service.get_remote_url(&repo_path, &remote_name)?;
let git_host = GitHostService::from_remote_url(&remote_url)?;
let pr_info = git_host.create_pr(&repo_path, &pr_request).await?;
// Get provider kind (infallible since already constructed)
let provider = git_host.provider_kind();
```
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
All cleaned up. Here's the final architecture:
**`git_host/mod.rs`:**
```rust
pub trait GitHostProvider: Send + Sync {
async fn create_pr(&self, repo_path: &Path, request: &CreatePrRequest) -> Result<PullRequestInfo, GitHostError>;
async fn get_pr_status(&self, pr_url: &str) -> Result<PullRequestInfo, GitHostError>;
async fn list_prs_for_branch(&self, repo_path: &Path, branch_name: &str) -> Result<Vec<PullRequestInfo>, GitHostError>;
async fn get_pr_comments(&self, repo_path: &Path, pr_number: i64) -> Result<Vec<UnifiedPrComment>, GitHostError>;
fn provider_kind(&self) -> ProviderKind;
}
pub enum GitHostService {
GitHub(GitHubProvider),
AzureDevOps(AzureDevOpsProvider),
}
impl GitHostService {
pub fn from_url(url: &str) -> Result<Self, GitHostError>
}
```
**Changes made:**
1. Removed `check_auth` and `get_repo_info` from the public trait - they're now private methods on each provider
2. Consolidated `from_pr_url` and `from_remote_url` into a single `from_url` constructor
3. The trait now has exactly 5 methods - the minimal public API needed by call sites
**Call site usage is clean:**
```rust
// From any URL (PR URL or remote URL)
let git_host = GitHostService::from_url(&url)?;
// Use the trait methods
let pr_info = git_host.create_pr(&repo_path, &request).await?;
let status = git_host.get_pr_status(&pr_url).await?;
let prs = git_host.list_prs_for_branch(&repo_path, &branch).await?;
let comments = git_host.get_pr_comments(&repo_path, pr_number).await?;
let kind = git_host.provider_kind();
```
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
Done. The refactoring is complete:
1. **Created `GitHubRepoInfo`** in `git_host/github/cli.rs` - a simple struct with `owner` and `repo_name`
2. **Created `AzureRepoInfo`** in `git_host/azure/cli.rs` - a struct with `organization_url`, `project`, `project_id`, `repo_name`, `repo_id`
3. **Updated both providers** to use their respective provider-specific structs directly, removing all the match arms and error cases for "wrong variant" that could never happen
4. **Removed the shared `RepoInfo` enum** from `types.rs` and the public exports
The code is cleaner now - each provider has its own internal representation of repo info, and there's no shared enum that requires impossible error handling paths.
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
The fix compiles. The issue was that `get_remote_name_from_branch_name` was calling `get_remote_from_branch_ref` which uses `repo.branch_remote_name()` - this git2 function requires the branch to have upstream tracking configured. For local branches that haven't been pushed yet (or don't have tracking set), this fails with the error you saw.
The fix changes the behavior to fall back to the default remote (typically "origin") when the branch doesn't have upstream tracking configured. This makes sense because:
1. Most repos only have one remote anyway
2. When creating a PR for a local branch, we need to push to *some* remote - the default is the sensible choice
3. The original code computed `default_remote` but never actually used it as a fallback
Done. Added `get_remote_url_from_branch_or_default` to `GitService` and updated all 4 call sites. The pattern is now:
```rust
let remote_url = deployment
.git()
.get_remote_url_from_branch_or_default(&repo_path, &branch)?;
let git_host = git_host::GitHostService::from_url(&remote_url)?;
```
Two lines instead of four, and the function name makes the fallback behavior explicit.
Done. Removed the unnecessary provider detection checks from both `push_task_attempt_branch` and `force_push_task_attempt_branch`. The push operations now just push directly - git push works with any remote regardless of whether we support it as a PR provider. Auth errors from git push itself (SSH keys, HTTPS credentials) still come through `GitServiceError::GitCLI(...)` and propagate to the frontend the same way.
* All done. Removed the low-value comments:
- "Create git host from the PR URL"
- "Fetch comments"
- "Get the URL for a remote (e.g., "origin")."
- "Azure DevOps-specific repository information"
- "GitHub-specific repository information"
- "Get the remote URL for the branch to detect the provider" (2 places)
- "Get repository info from the GitHub CLI"
- "Get repository info from the Azure CLI"
- "Check authentication status" (2 places)
Kept comments that provide actual value like the fallback behavior explanation on `get_remote_url_from_branch_or_default`.
All done. Removed the low-value comments:
- "Create git host from the PR URL"
- "Fetch comments"
- "Get the URL for a remote (e.g., "origin")."
- "Azure DevOps-specific repository information"
- "GitHub-specific repository information"
- "Get the remote URL for the branch to detect the provider" (2 places)
- "Get repository info from the GitHub CLI"
- "Get repository info from the Azure CLI"
- "Check authentication status" (2 places)
- "Get PR status from URL (auto-detects provider)"
- "High-level errors originating from the Azure CLI."
- "High-level errors originating from the GitHub CLI."
- "Azure DevOps hosting provider implementation"
- "GitHub hosting provider implementation"
- "Newtype wrapper for invoking the `gh` command."
- "Newtype wrapper for invoking the `az` command."
- "Trait for git hosting provider operations (GitHub, Azure DevOps, etc.)"
- All the trait method doc comments (Create a pull request, Get PR status, etc.)
- "Git hosting service - dispatches to the appropriate provider."
- "Create a GitHostService by detecting the provider from a URL."
- Simplified the Azure DevOps pattern comments in detection.rs
* Done. Here's a summary of the fix:
**Problem**: `get_repo_info` used `az repos list --detect true` which returns ALL repos in the Azure DevOps project, then just took the first one. This was wrong for multi-repo projects.
**Solution**:
1. `AzureDevOpsProvider` now stores the remote URL passed to `from_url()`
2. `get_repo_info` now takes the remote URL and filters the repo list to find the matching repo using `remote_urls_match()`
3. Added `remote_urls_match()` helper that normalizes URLs (lowercase, strips trailing `/` and `.git`) for comparison
4. Added `extract_organization_url()` to get the Azure organization URL from a remote URL (supports both `dev.azure.com` and legacy `visualstudio.com` formats)
5. Removed the now-unused `extract_org_from_remote_url()` function
The flow is now:
- Call site gets remote URL via `get_remote_url_from_branch_or_default()`
- Creates `GitHostService::from_url(&remote_url)` which stores the URL on `AzureDevOpsProvider`
- When `get_repo_info` is called, it lists all repos in the project and finds the one matching the stored remote URL
Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
* Done. Summary of changes:
1. **`pr.rs:get_pr_comments`** - Now gets the remote URL from `workspace_repo.target_branch` instead of using the PR URL to create the `GitHostService`
2. **`azure/cli.rs`**:
- Added `ssh_url` field to `AzRepoListItem`
- Updated `get_repo_info` to detect if the input URL is SSH (`starts_with("git@")`) and compare against the appropriate field (`ssh_url` vs `remote_url`)
- Renamed `remote_urls_match` to `urls_match` since it now handles both HTTPS and SSH URLs
- Added SSH URL test case
* Build passes. The simple fix handles both SSH formats without adding a dependency.
* Build passes. Now `ssh_url` is optional - if it's not in the response and the user has an SSH remote configured, they'll get a clear error message ("No repo found matching remote URL: ...").
* Done. The `urls_match` function now normalizes `ssh://` prefix to scp-style before comparing.
* Done. Summary of changes:
1. **Removed `remote_url` field from `AzureDevOpsProvider`** - no longer stored on the struct
2. **Updated `GitHostProvider` trait** - added `remote_url: &str` parameter to:
- `create_pr`
- `list_prs_for_branch`
- `get_pr_comments`
3. **Updated `AzureDevOpsProvider`** - methods now accept `remote_url` and pass it to `get_repo_info`
4. **Updated `GitHubProvider`** - methods accept `_remote_url` (unused, since gh CLI auto-detects)
5. **Updated call sites in `pr.rs`** - all three methods now pass `remote_url`:
- `create_pr` passes `&remote_url`
- `list_prs_for_branch` passes `&remote_url`
- `get_pr_comments` passes `&remote_url`
6. **Fixed `attach_existing_pr`** - now uses `workspace_repo.target_branch` instead of `workspace.branch` when getting the remote URL (consistent with other call sites)
* Cleanup script changes for workspace 28eb9564-f95a-42e0-8cf2-5228c2ffc0bf
* Update frontend remote copy (vibe-kanban a7bf13b6)
We added azure support but in the frontend many things have github specific wording. Please fix!
* Done. I've addressed the PR comment by:
1. **Renamed `CreatePrError` to `PrError`** - A more generic name that works for both `create_pr` and `attach_existing_pr`
2. **Updated `attach_existing_pr`** to mirror the error handling from `create_pr`:
- Changed return type from `ApiResponse<AttachPrResponse>` to `ApiResponse<AttachPrResponse, PrError>`
- Added error handling for `GitHostService::from_url()` - handles `UnsupportedProvider` and `CliNotInstalled`
- Added error handling for `list_prs_for_branch()` - handles `CliNotInstalled` and `AuthFailed`
3. **Updated `generate_types.rs`** to use the renamed `PrError`
4. **Updated `frontend/src/lib/api.ts`** to use `PrError` instead of `CreatePrError`
5. **Regenerated TypeScript types** and verified all checks pass
This commit is contained in:
@@ -28,6 +28,7 @@ dirs = "5.0"
|
||||
git2 = { workspace = true }
|
||||
tempfile = "3.21"
|
||||
async-trait = { workspace = true }
|
||||
enum_dispatch = "0.3.13"
|
||||
rust-embed = "8.2"
|
||||
ignore = "0.4"
|
||||
regex = "1.11.1"
|
||||
|
||||
Reference in New Issue
Block a user