Skip to content

Commit be78598

Browse files
committed
Allow for any git repo to be checked out
1 parent 3eb2354 commit be78598

File tree

3 files changed

+108
-27
lines changed

3 files changed

+108
-27
lines changed

gitlab-runner/examples/demo-runner.rs

+27
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,33 @@ impl Run {
123123
}
124124
Ok(())
125125
}
126+
"clone" => {
127+
let url = match p.next() {
128+
Some(url) => url,
129+
_ => {
130+
outputln!("Missing repo url as first argument");
131+
return Err(());
132+
}
133+
};
134+
let path = match p.next() {
135+
Some(path) => self.job.build_dir().join(path),
136+
_ => {
137+
outputln!("Missing repo output path as second argument");
138+
return Err(());
139+
}
140+
};
141+
let reference = p.next();
142+
143+
Job::checkout_repo_inner(
144+
path.as_path(),
145+
url,
146+
reference,
147+
std::iter::empty::<&str>(),
148+
Some(1),
149+
)
150+
.map_err(|e| outputln!("Failed to checkout repo: {}", e.to_string()))?;
151+
Ok(())
152+
}
126153
"checkout" => {
127154
let path = match p.next() {
128155
Some(path) => self.job.build_dir().join(path),

gitlab-runner/src/client.rs

+10
Original file line numberDiff line numberDiff line change
@@ -291,15 +291,25 @@ pub enum GitCheckoutError {
291291
#[error(transparent)]
292292
GitObjectPeel(#[from] gix::object::peel::to_kind::Error),
293293
#[error(transparent)]
294+
GitObjsFindExisting(#[from] gix::objs::find::existing::Error),
295+
#[error(transparent)]
294296
GitRefIterInit(#[from] gix::reference::iter::init::Error),
295297
#[error(transparent)]
298+
GitRefFindExisting(#[from] gix::reference::find::existing::Error),
299+
#[error(transparent)]
300+
GitRefFind(#[from] gix::reference::find::Error),
301+
#[error(transparent)]
302+
GetRefPeel(#[from] gix::reference::peel::Error),
303+
#[error(transparent)]
296304
GitPackedBufferOpen(#[from] gix::refs::packed::buffer::open::Error),
297305
#[error(transparent)]
298306
GitRefspecParse(#[from] gix::refspec::parse::Error),
299307
#[error(transparent)]
300308
GitHashDecode(#[from] gix::hash::decode::Error),
301309
#[error(transparent)]
302310
GitWorktreeCheckout(#[from] gix::worktree::state::checkout::Error),
311+
#[error(transparent)]
312+
GitHeadPeel(#[from] gix::head::peel::Error),
303313
#[error("Job does not allow fetch")]
304314
FetchNotAllowed,
305315
#[error("Failed to find commit")]

gitlab-runner/src/job.rs

+71-27
Original file line numberDiff line numberDiff line change
@@ -355,22 +355,22 @@ impl Job {
355355
Self::checkout_repo_inner(
356356
repo_dir.path(),
357357
&self.response.git_info.repo_url,
358-
&self.response.git_info.sha,
359-
self.response.git_info.refspecs.iter(),
360-
self.response.git_info.depth,
358+
Some(&self.response.git_info.sha),
359+
&self.response.git_info.refspecs,
360+
Some(self.response.git_info.depth),
361361
)?;
362362

363363
Ok(repo_dir.into_path())
364364
}
365365

366366
/// Fetch and checkout worktree for job
367367
#[allow(clippy::result_large_err)]
368-
fn checkout_repo_inner(
368+
pub fn checkout_repo_inner(
369369
repo_dir: &Path,
370370
repo_url: &str,
371-
sha: &str,
372-
refspecs: impl Iterator<Item = impl AsRef<str>>,
373-
depth: u32,
371+
head_ref: Option<&str>,
372+
refspecs: impl IntoIterator<Item = impl AsRef<str>>,
373+
depth: Option<u32>,
374374
) -> Result<(), GitCheckoutError> {
375375
let should_interrupt = AtomicBool::new(false);
376376
let mut progress = progress::Discard;
@@ -384,48 +384,92 @@ impl Job {
384384
)?;
385385

386386
// Specify which refspecs are fetched from remote
387-
let refspecs = refspecs
387+
let mut refspecs = refspecs
388+
.into_iter()
388389
.map(|s| {
389390
refspec::parse(s.as_ref().into(), refspec::parse::Operation::Fetch)
390391
.map(|r| r.to_owned())
391392
})
392393
.collect::<Result<Vec<_>, _>>()?;
393394

394-
let mut fetch_opts = remote::ref_map::Options::default();
395-
fetch_opts.extra_refspecs.extend(refspecs);
396-
fetch = fetch.with_fetch_options(fetch_opts);
395+
let mut sha = None;
396+
397+
if let Some(head_ref) = head_ref {
398+
if let Ok(object_id) = gix::ObjectId::from_hex(head_ref.as_bytes()) {
399+
// TODO: I need to specify that the sha should be fetched from remote, right?
400+
// Is there a format for a sha as a refspec?
401+
if let Ok(refspec) = refspec::parse(
402+
format!("+{head_ref}").as_str().into(),
403+
refspec::parse::Operation::Fetch,
404+
) {
405+
refspecs.push(refspec.to_owned());
406+
}
407+
sha = Some(object_id);
408+
} else {
409+
// TODO: For some reason this doesn't create refs/heads/{reference}
410+
if let Ok(refspec) = refspec::parse(
411+
format!("+refs/heads/{head_ref}:refs/remotes/origin/{head_ref}")
412+
.as_str()
413+
.into(),
414+
refspec::parse::Operation::Fetch,
415+
) {
416+
refspecs.push(refspec.to_owned());
417+
}
418+
}
419+
}
397420

398-
if depth > 0 {
399-
fetch = fetch.with_shallow(remote::fetch::Shallow::DepthAtRemote(
400-
NonZeroU32::new(depth).unwrap(),
401-
))
402-
};
421+
if !refspecs.is_empty() {
422+
let mut fetch_opts = remote::ref_map::Options::default();
423+
fetch_opts.extra_refspecs.extend(refspecs);
424+
fetch = fetch.with_fetch_options(fetch_opts);
425+
}
426+
427+
if let Some(depth) = depth {
428+
if depth > 0 {
429+
fetch = fetch.with_shallow(remote::fetch::Shallow::DepthAtRemote(
430+
NonZeroU32::new(depth).unwrap(),
431+
));
432+
}
433+
}
403434

404435
let checkout_progress = progress.add_child("checkout".to_string());
405436
let (checkout, _outcome) =
406437
fetch.fetch_then_checkout(checkout_progress, &should_interrupt)?;
407438

408439
let repo = checkout.persist();
409440

410-
// Checkout worktree at specific sha
411-
let sha = gix::ObjectId::from_hex(sha.as_bytes())?;
412-
let root_tree_id = repo
413-
.try_find_object(sha)?
414-
.ok_or(GitCheckoutError::MissingCommit)?;
441+
let root_tree_id = if let Some(sha) = sha {
442+
// Checkout worktree at specific sha
443+
repo.try_find_object(sha)?
444+
.ok_or(GitCheckoutError::MissingCommit)?
445+
} else if let Some(reference) = head_ref {
446+
repo
447+
// TODO: This should be refs/heads/{reference} but it doesn't exist
448+
.try_find_reference(format!("refs/remotes/origin/{reference}").as_str())?
449+
.ok_or(GitCheckoutError::MissingCommit)?
450+
.peel_to_id_in_place()?
451+
.object()?
452+
} else {
453+
// Checkout head
454+
repo.head()?
455+
.try_peel_to_id_in_place()?
456+
.ok_or(GitCheckoutError::MissingCommit)?
457+
.object()?
458+
};
415459
let root_tree = root_tree_id.peel_to_tree()?.id;
416460

417-
let workdir = repo.work_dir().ok_or_else(|| {
418-
clone::checkout::main_worktree::Error::BareRepository {
419-
git_dir: repo.git_dir().to_owned(),
420-
}
421-
})?;
422-
423461
let index = index::State::from_tree(&root_tree, &repo.objects, Default::default())
424462
.map_err(|err| clone::checkout::main_worktree::Error::IndexFromTree {
425463
id: root_tree,
426464
source: err,
427465
})?;
428466

467+
let workdir = repo.work_dir().ok_or_else(|| {
468+
clone::checkout::main_worktree::Error::BareRepository {
469+
git_dir: repo.git_dir().to_owned(),
470+
}
471+
})?;
472+
429473
let files_progress = progress.add_child_with_id(
430474
"files".to_string(),
431475
clone::checkout::main_worktree::ProgressId::CheckoutFiles.into(),

0 commit comments

Comments
 (0)