diff --git a/src/git.rs b/src/git.rs index 8eada5c9b11..8b96d12c1f7 100644 --- a/src/git.rs +++ b/src/git.rs @@ -1,4 +1,6 @@ +use anyhow::{anyhow, Context}; use std::collections::HashMap; +use std::io::Write; use std::path::{Path, PathBuf}; use swirl::PerformError; @@ -48,6 +50,45 @@ impl Credentials { } } } + + /// Write the SSH key to a temporary file and return the path. The file is + /// deleted once the returned path is dropped. + /// + /// This function can be used when running `git push` instead of using the + /// `git2` crate for pushing commits to remote git servers. + /// + /// Note: On Linux this function creates the temporary file in `/dev/shm` to + /// avoid writing it to disk. + /// + /// # Errors + /// + /// - If non-SSH credentials are use, `Err` is returned. + /// - If creation of the temporary file fails, `Err` is returned. + /// + pub fn write_temporary_ssh_key(&self) -> anyhow::Result { + let key = match self { + Credentials::Ssh { key } => key, + _ => return Err(anyhow!("SSH key not available")), + }; + + let dir = if cfg!(target_os = "linux") { + // When running on production, ensure the file is created in tmpfs and not persisted to disk + "/dev/shm".into() + } else { + // For other platforms, default to std::env::tempdir() + std::env::temp_dir() + }; + + let mut temp_key_file = tempfile::Builder::new() + .tempfile_in(dir) + .context("Failed to create temporary file")?; + + temp_key_file + .write_all(key.as_bytes()) + .context("Failed to write SSH key to temporary file")?; + + Ok(temp_key_file.into_temp_path()) + } } #[derive(Serialize, Deserialize, Debug)] diff --git a/src/worker/git.rs b/src/worker/git.rs index f4ac6253932..b2d1cdd8928 100644 --- a/src/worker/git.rs +++ b/src/worker/git.rs @@ -1,11 +1,10 @@ use crate::background_jobs::Environment; -use crate::git::{Crate, Credentials}; +use crate::git::Crate; use crate::schema; use anyhow::Context; use chrono::Utc; use diesel::prelude::*; use std::fs::{self, OpenOptions}; -use std::io::prelude::*; use swirl::PerformError; #[swirl::background_job] @@ -99,23 +98,7 @@ pub fn squash_index(env: &Environment) -> Result<(), PerformError> { // Shell out to git because libgit2 does not currently support push leases - let key = match &repo.credentials { - Credentials::Ssh { key } => key, - Credentials::Http { .. } => { - return Err(String::from("squash_index: Password auth not supported").into()) - } - _ => return Err(String::from("squash_index: Could not determine credentials").into()), - }; - - // When running on production, ensure the file is created in tmpfs and not persisted to disk - #[cfg(target_os = "linux")] - let mut temp_key_file = tempfile::Builder::new().tempfile_in("/dev/shm")?; - - // For other platforms, default to std::env::tempdir() - #[cfg(not(target_os = "linux"))] - let mut temp_key_file = tempfile::Builder::new().tempfile()?; - - temp_key_file.write_all(key.as_bytes())?; + let temp_key_path = repo.credentials.write_temporary_ssh_key()?; let checkout_path = repo.checkout_path.path(); let output = std::process::Command::new("git") @@ -124,7 +107,7 @@ pub fn squash_index(env: &Environment) -> Result<(), PerformError> { "GIT_SSH_COMMAND", format!( "ssh -o StrictHostKeyChecking=accept-new -i {}", - temp_key_file.path().display() + temp_key_path.display() ), ) .args(&[