Compare commits

...
Author SHA1 Message Date
beardthelion f38e00e7fc chore: refresh PR head 2026-11-05 10:48:05 -06:00
beardthelion 227246fa11 test(gl): cover a non-UTF-8 TMPDIR end to end via a child process 2026-09-10 21:31:36 -05:00
beardthelion c674f520ed fix(gl): pass the mirror temp path to git as an OS string
The clone destination went through to_str().unwrap(), so a TMPDIR with
non-UTF-8 bytes panicked before git started. Pass the Path directly and
add a Unix regression test cloning into a non-UTF-8 directory name.

Closes #417
2026-09-10 17:30:02 -05:00
Kevin Codex bfc44f926d Merge pull request #381 from Gitlawb/fix/gl-whoami-capped-body-read
fix(gl): read whoami error bodies through CappedBody.text
2026-08-26 13:26:50 +08:00
beardthelion 65f5989a1d test(node): pin owner-push off in receive-pack lock tests
These lock-path proofs use non-owner proof DIDs. With enforce_owner_push
defaulting on, they fail before receive-pack runs.
2026-08-25 10:27:48 -05:00
beardthelion 4725146db2 fix(gl): read whoami error bodies through CappedBody.text
#173 changed read_body_capped to return CappedBody; peer.rs and sync.rs
were updated but whoami.rs was not, leaving main red after the #223 merge.
2026-08-25 10:04:20 -05:00
3 changed files with 104 additions and 9 deletions

No files matched your search

+9
View File
@@ -6260,6 +6260,9 @@ mod tests {
// Long enough that the git-service timeout is never what ends this push; the
// disconnect is.
cfg.git_service_timeout_secs = 600;
// Lock-path test, not owner-push authorization: pin off so the default-on gate
// does not reject the proof DID before receive-pack runs.
cfg.enforce_owner_push = false;
state.config = std::sync::Arc::new(cfg);
state
.db
@@ -6430,6 +6433,9 @@ mod tests {
None,
crate::git::repo_store::build_lock_pool(&pool, 4, std::time::Duration::from_secs(5)),
);
let mut cfg = (*state.config).clone();
cfg.enforce_owner_push = false;
state.config = std::sync::Arc::new(cfg);
state
.db
.upsert_mirror_repo(owner, name, "/tmp/z6succ-sc1", None, false)
@@ -6516,6 +6522,9 @@ mod tests {
let owner = "z6lockpool";
let name = "lp1";
let mut state = crate::test_support::test_state(pool.clone()).await;
let mut cfg = (*state.config).clone();
cfg.enforce_owner_push = false;
state.config = std::sync::Arc::new(cfg);
// One lock-pool connection, short checkout timeout so the exhaustion surfaces
// promptly rather than at the handler's own acquire deadline.
state.repo_store = crate::git::repo_store::RepoStore::new(
+94 -8
View File
@@ -80,14 +80,7 @@ pub async fn run(args: MirrorArgs) -> Result<()> {
let _guard = TmpGuard(tmp_root);
println!("Cloning source (this may take a while for large repos)...");
let clone_status = Command::new("git")
.args(["clone", "--mirror", &source, mirror_path.to_str().unwrap()])
.status()
.context("failed to run git clone — is git installed?")?;
if !clone_status.success() {
bail!("git clone --mirror failed\nCheck that the source URL is accessible: {source}");
}
clone_mirror(&source, &mirror_path)?;
// ── 4. Create the repo on gitlawb ─────────────────────────────────────
println!("Creating repo on gitlawb node...");
@@ -139,6 +132,25 @@ pub async fn run(args: MirrorArgs) -> Result<()> {
Ok(())
}
/// `git clone --mirror <source> <dest>`. The destination is passed as an
/// OS-native path so a non-UTF-8 temp dir (e.g. a TMPDIR with non-UTF-8 bytes)
/// does not panic on a `to_str()` conversion.
fn clone_mirror(source: &str, dest: &std::path::Path) -> Result<()> {
// allow-unbounded-git: gl is the client CLI; this clone runs in the user's
// terminal with no server-side permit or disconnect to survive. The
// bounded-runner rule governs node request handlers. Same spawn as before,
// only relocated into this helper.
let status = Command::new("git")
.args(["clone", "--mirror", source])
.arg(dest)
.status()
.context("failed to run git clone — is git installed?")?;
if !status.success() {
bail!("git clone --mirror failed\nCheck that the source URL is accessible: {source}");
}
Ok(())
}
/// Extract the repo name from a git URL.
/// - `https://github.com/owner/repo` → `"repo"`
/// - `https://github.com/owner/repo.git` → `"repo"`
@@ -221,6 +233,80 @@ mod tests {
assert_eq!(extract_repo_name("https://example.com/.git"), None);
}
// #417: a non-UTF-8 destination path must reach git as an OS-native arg, not
// panic on a `to_str()` conversion.
#[cfg(unix)]
#[test]
fn test_clone_mirror_non_utf8_dest() {
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
let tmp = tempfile::TempDir::new().unwrap();
let src = tmp.path().join("src.git");
let st = Command::new("git")
.args(["init", "--bare"])
.arg(&src)
.status()
.unwrap();
assert!(st.success());
let dest = tmp.path().join(OsStr::from_bytes(b"mirror-\xffdest"));
clone_mirror(src.to_str().unwrap(), &dest).unwrap();
assert!(dest.join("HEAD").exists());
}
// #417 end-to-end: the real trigger is `std::env::temp_dir()` itself
// returning a non-UTF-8 path (a TMPDIR with non-UTF-8 bytes), because the
// mirror dest is built from it. Env is process-global, so this test
// re-execs the test binary in a child with a non-UTF-8 TMPDIR rather than
// mutating this process's env.
#[cfg(unix)]
#[test]
fn test_clone_mirror_under_non_utf8_tmpdir() {
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
if std::env::var_os("GL_MIRROR_NONUTF8_TMPDIR").is_none() {
// Parent: a source repo under a normal path, a TMPDIR whose name
// is not UTF-8, and a child run of this same test under that env.
let parent = tempfile::TempDir::new().unwrap();
let src = parent.path().join("src.git");
let st = Command::new("git")
.args(["init", "--bare"])
.arg(&src)
.status()
.unwrap();
assert!(st.success());
let bad_tmp = parent.path().join(OsStr::from_bytes(b"tmp-\xff"));
std::fs::create_dir_all(&bad_tmp).unwrap();
let status = Command::new(std::env::current_exe().unwrap())
.args([
"mirror::tests::test_clone_mirror_under_non_utf8_tmpdir",
"--exact",
])
.env("GL_MIRROR_NONUTF8_TMPDIR", "1")
.env("GL_MIRROR_SRC", &src)
.env("TMPDIR", &bad_tmp)
.status()
.unwrap();
assert!(status.success(), "child failed under non-UTF-8 TMPDIR");
return;
}
// Child: the same path shape run() computes — temp_dir() joined with
// the mirror dest. It must be non-UTF-8 or the test proves nothing.
let src = std::env::var("GL_MIRROR_SRC").unwrap();
let tmp_root = std::env::temp_dir().join("gl-mirror-child");
let dest = tmp_root.join("repo");
assert!(
dest.to_str().is_none(),
"fixture broken: dest under a non-UTF-8 TMPDIR must be non-UTF-8"
);
clone_mirror(&src, &dest).unwrap();
assert!(dest.join("HEAD").exists());
}
#[tokio::test]
async fn test_create_repo_conflict_error() {
let mut server = mockito::Server::new_async().await;
+1 -1
View File
@@ -60,7 +60,7 @@ pub(crate) async fn run_to_writer(args: WhoamiArgs, w: &mut impl std::io::Write)
}
Ok(resp) => {
let status = resp.status();
let raw = read_body_capped(resp, 8 * 1024).await;
let raw = read_body_capped(resp, 8 * 1024).await.text;
let msg = serde_json::from_str::<Value>(&raw)
.ok()
.and_then(|v| {