Skip to content

Commit 3b280fb

Browse files
committed
Shell out to git because libgit2 does not support push leases
Password authentication is not supported as we do not use that in production or staging. The SSH key is written to a temporary file which is deleted after the push operation completes. The SSH host key is automatically accepted as long as no conflicting key already exists.
1 parent 3bb32cf commit 3b280fb

File tree

1 file changed

+44
-8
lines changed

1 file changed

+44
-8
lines changed

src/git.rs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use std::collections::HashMap;
2-
use std::fs::{self, OpenOptions};
2+
use std::fs::{self, File, OpenOptions};
3+
use std::io::prelude::*;
34
use std::path::{Path, PathBuf};
45

56
use chrono::Utc;
67
use swirl::PerformError;
7-
use tempfile::{Builder, TempDir};
8+
use tempfile::TempDir;
89
use url::Url;
910

1011
use crate::background_jobs::Environment;
@@ -148,7 +149,7 @@ pub struct Repository {
148149

149150
impl Repository {
150151
pub fn open(repository_config: &RepositoryConfig) -> Result<Self, PerformError> {
151-
let checkout_path = Builder::new().prefix("git").tempdir()?;
152+
let checkout_path = tempfile::Builder::new().prefix("git").tempdir()?;
152153

153154
let repository = git2::build::RepoBuilder::new()
154155
.fetch_options(Self::fetch_options(&repository_config.credentials))
@@ -383,24 +384,59 @@ pub fn squash_index(env: &Environment) -> Result<(), PerformError> {
383384
println!("Squashing the index into a single commit.");
384385

385386
let now = Utc::now().format("%Y-%m-%d");
386-
let head = repo.head_oid()?;
387+
let original_head = repo.head_oid()?;
387388
let msg = format!("Collapse index into one commit\n\n\
388389
389390
Previous HEAD was {}, now on the `snapshot-{}` branch\n\n\
390391
391392
More information about this change can be found [online] and on [this issue].\n\n\
392393
393394
[online]: https://internals.rust-lang.org/t/cargos-crate-index-upcoming-squash-into-one-commit/8440\n\
394-
[this issue]: https://github.com/rust-lang/crates-io-cargo-teams/issues/47", head, now);
395+
[this issue]: https://github.com/rust-lang/crates-io-cargo-teams/issues/47", original_head, now);
395396

396397
// Create a snapshot branch of current `HEAD`.
397398
repo.push(&format!("HEAD:refs/heads/snapshot-{}", now))?;
398399

399400
repo.squash_to_single_commit(&msg)?;
400401

401-
// Because this will not be a fast-forward push, `+` is added to the
402-
// beginning of the refspec to force the push.
403-
repo.push("+HEAD:refs/heads/master")?;
402+
// Shell out to git because libgit2 does not currently support push leases
403+
404+
let key = match &repo.credentials {
405+
Credentials::Ssh { key } => key,
406+
Credentials::Http { .. } => {
407+
return Err(String::from("squash_index: Password auth not supported").into())
408+
}
409+
_ => return Err(String::from("squash_index: Could not determine credentials").into()),
410+
};
411+
412+
let temp_key_file = tempfile::Builder::new().tempfile()?;
413+
let mut file = File::create(&temp_key_file)?;
414+
file.write_all(key.as_bytes())?;
415+
drop(file);
416+
417+
let checkout_path = repo.checkout_path.path();
418+
let output = std::process::Command::new("git")
419+
.current_dir(checkout_path)
420+
.env(
421+
"GIT_SSH_COMMAND",
422+
format!(
423+
"ssh -o StrictHostKeyChecking=accept-new -i {}",
424+
temp_key_file.path().display()
425+
),
426+
)
427+
.args(&[
428+
"push",
429+
"origin",
430+
"HEAD:refs/heads/master",
431+
&format!("--force-with-lease=refs/heads/master:{}", original_head),
432+
])
433+
.output()?;
434+
435+
if !output.status.success() {
436+
let stderr = String::from_utf8_lossy(&output.stderr);
437+
let message = format!("Running git command failed with: {}", stderr);
438+
return Err(message.into());
439+
}
404440

405441
println!("The index has been successfully squashed.");
406442

0 commit comments

Comments
 (0)