Skip to main content

max / makenotwork

Fix git auto-create: use direct init with shared=group Run git init --bare --shared=group directly as mnw-cli user instead of via sudo. mnw-cli is in the git group and parent dirs have setgid, so repos get git group ownership with group-writable permissions. Set safe.directory=* for the git service user (standard for git hosting). Avoids systemd security restrictions that block sudo child processes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-05-03 00:40 UTC
Commit: 654cb2d22ab69031c901fec3bfa1c92eb0f9925e
Parent: 44cfcde
2 files changed, +16 insertions, -22 deletions
@@ -179,33 +179,23 @@ done
179 179
180 180 /// Install the post-receive hook in a bare repository.
181 181 pub async fn install_post_receive_hook(
182 - git_user: &str,
182 + _git_user: &str,
183 183 repo_path: &str,
184 184 token: &str,
185 185 ) -> anyhow::Result<()> {
186 186 let hook_content = POST_RECEIVE_HOOK.replace("__TOKEN__", token);
187 - let hook_path = format!("{repo_path}/hooks/post-receive");
187 + let hook_path = std::path::PathBuf::from(repo_path).join("hooks/post-receive");
188 188
189 - // Write hook content via sudo tee (runs as git user)
190 - let mut child = tokio::process::Command::new("sudo")
191 - .args(["-u", git_user, "tee", &hook_path])
192 - .stdin(std::process::Stdio::piped())
193 - .stdout(std::process::Stdio::null())
194 - .spawn()?;
189 + // Write directly — mnw-cli is in the git group, setgid dir gives correct ownership
190 + tokio::fs::write(&hook_path, hook_content.as_bytes()).await?;
195 191
196 - if let Some(mut stdin) = child.stdin.take() {
197 - use tokio::io::AsyncWriteExt;
198 - stdin.write_all(hook_content.as_bytes()).await?;
192 + #[cfg(unix)]
193 + {
194 + use std::os::unix::fs::PermissionsExt;
195 + tokio::fs::set_permissions(&hook_path, std::fs::Permissions::from_mode(0o755)).await?;
199 196 }
200 - child.wait().await?;
201 -
202 - // Make executable
203 - let _ = tokio::process::Command::new("sudo")
204 - .args(["-u", git_user, "chmod", "+x", &hook_path])
205 - .status()
206 - .await;
207 197
208 - tracing::debug!(path = %hook_path, "installed post-receive hook");
198 + tracing::debug!(path = %hook_path.display(), "installed post-receive hook");
209 199 Ok(())
210 200 }
211 201
@@ -301,10 +301,14 @@ impl russh::server::Handler for MnwHandler {
301 301 // The server only registers the repo in the DB — we create
302 302 // it here as the git user so ownership is correct.
303 303 if !std::path::Path::new(&auth.repo_path).exists() {
304 - match tokio::process::Command::new("sudo")
305 - .args(["-u", &self.git_user, "git", "init", "--bare", &auth.repo_path])
304 + // Run git init directly (not via sudo) — mnw-cli is in the
305 + // git group and the parent dir has setgid, so the repo
306 + // gets git group ownership. Avoids systemd security
307 + // restrictions that block sudo child processes.
308 + match tokio::process::Command::new("git")
309 + .args(["init", "--bare", "--shared=group", &auth.repo_path])
306 310 .stdout(std::process::Stdio::null())
307 - .stderr(std::process::Stdio::piped())
311 + .stderr(std::process::Stdio::null())
308 312 .status()
309 313 .await
310 314 {