diff --git a/CHANGELOG.md b/CHANGELOG.md index 277d05d..5748008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to Braid are recorded here. The project follows Semantic Versioning once release artifacts are published. +## [0.3.1] - unreleased + +### Fixed + +- PR worktree provisioning fetched through libgit2, which ignores the + operator's credential helpers and proxy configuration and failed on real + networks ("no TLS stream available"). The fetch now uses the configured + system `git` executable; libgit2 remains for local reference/worktree + operations. + ## [0.3.0] - 2026-08-31 ### Added diff --git a/src/group/pr_agent.rs b/src/group/pr_agent.rs index e0d1ad9..369e628 100644 --- a/src/group/pr_agent.rs +++ b/src/group/pr_agent.rs @@ -440,6 +440,7 @@ pub(crate) fn provision_pr_agent_worktree( target: &target, repository: &config.github.repository, remote: "origin", + git: &config.tools.git, head_ref: &prepared.head_ref, local_branch: &local_branch, })?; diff --git a/src/worktree.rs b/src/worktree.rs index af95243..3b8cff3 100644 --- a/src/worktree.rs +++ b/src/worktree.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use git2::{ErrorClass, ErrorCode, Repository, WorktreeAddOptions}; +use git2::{ErrorClass, ErrorCode, Repository}; use thiserror::Error; #[derive(Debug, Error)] @@ -27,6 +27,10 @@ pub struct WorktreeRequest<'a> { pub target: &'a Path, pub repository: &'a str, pub remote: &'a str, + /// System `git` executable used for the network fetch: it honors the + /// operator's credential helpers and proxy configuration, which libgit2 + /// does not. + pub git: &'a Path, pub head_ref: &'a str, pub local_branch: &'a str, } @@ -62,31 +66,45 @@ pub fn provision(request: &WorktreeRequest<'_>) -> Result Result<(), WorktreeError> { + let output = std::process::Command::new(request.git) + .arg("-C") + .arg(&source) + .args(args) + .output() + .map_err(|source_err| WorktreeError::Io { + path: source.clone(), + source: source_err, + })?; + if !output.status.success() { + return Err(WorktreeError::Git(format!( + "git {} failed: {}", + args.first().unwrap_or(&""), + String::from_utf8_lossy(&output.stderr).trim() + ))); + } + Ok(()) + }; + let remote_ref = format!("refs/remotes/{}/{}", request.remote, request.head_ref); + git(&[ + "fetch", + request.remote, + &format!("+refs/heads/{0}:{1}", request.head_ref, remote_ref), + ])?; + // libgit2's worktree add rejects remote-tracking references + // ("reference is not a branch"); the system git creates the + // generation-scoped local branch and the worktree in one step. + git(&[ + "worktree", + "add", + request + .target + .to_str() + .ok_or_else(|| WorktreeError::Git("worktree target path is not UTF-8".into()))?, + "-B", + request.local_branch, + &remote_ref, + ])?; Ok::<(), WorktreeError>(()) })?; verify_existing(request, &source)