Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,487 changes: 1,485 additions & 2 deletions Cargo.lock

Large diffs are not rendered by default.

11 changes: 4 additions & 7 deletions gitlab-runner-mock/src/api/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,13 @@ impl Respond for JobRequestResponder {
"project_name": "gitlab-test"
},
"git_info": {
"repo_url": "https://bla/dummy.git",
"repo_url": job.git_info().repo_url,
"ref": "custom",
"sha": "265c14cf140a66cfc61e40e4ab45c95ba8df5ed1",
"sha": job.git_info().sha,
"before_sha": "fc40ad32cdd36b814f07a540605110edc209a38c",
"ref_type": "branch",
"refspecs": [
"+265c14cf140a66cfc61e40e4ab45c95ba8df5ed1:refs/pipelines/120",
"+refs/heads/custom:refs/remotes/origin/custom"
],
"depth": 50
"refspecs": job.git_info().refspecs,
"depth": job.git_info().depth
},
"runner_info": {
"timeout": 3600,
Expand Down
34 changes: 34 additions & 0 deletions gitlab-runner-mock/src/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,28 @@ pub struct MockJobArtifact {
pub expire_in: Option<String>,
}

#[derive(Clone, Debug)]
pub struct MockJobGitInfo {
pub repo_url: String,
pub sha: String,
pub refspecs: Vec<String>,
pub depth: u32,
}

impl Default for MockJobGitInfo {
fn default() -> Self {
Self {
repo_url: "https://bla/dummy.git".to_string(),
sha: "265c14cf140a66cfc61e40e4ab45c95ba8df5ed1".to_string(),
refspecs: vec![
"+265c14cf140a66cfc61e40e4ab45c95ba8df5ed1:refs/pipelines/120".to_string(),
"+refs/heads/custom:refs/remotes/origin/custom".to_string(),
],
depth: 50,
}
}
}

#[derive(Clone, Debug)]
pub struct MockJob {
name: String,
Expand All @@ -126,6 +148,7 @@ pub struct MockJob {
steps: Vec<MockJobStep>,
dependencies: Vec<MockJob>,
artifacts: Vec<MockJobArtifact>,
git_info: MockJobGitInfo,
inner: Arc<Mutex<MockJobInner>>,
}

Expand Down Expand Up @@ -163,6 +186,7 @@ impl MockJob {
steps: Vec::new(),
dependencies: Vec::new(),
artifacts: Vec::new(),
git_info: MockJobGitInfo::default(),
inner: Arc::new(Mutex::new(MockJobInner {
state: MockJobState::Success,
state_updates: 2,
Expand Down Expand Up @@ -193,6 +217,10 @@ impl MockJob {
&self.artifacts
}

pub fn git_info(&self) -> &MockJobGitInfo {
&self.git_info
}

pub fn variables(&self) -> &[MockJobVariable] {
&self.variables
}
Expand Down Expand Up @@ -296,6 +324,7 @@ pub struct MockJobBuilder {
steps: Vec<MockJobStep>,
dependencies: Vec<MockJob>,
artifacts: Vec<MockJobArtifact>,
git_info: MockJobGitInfo,
}

impl MockJobBuilder {
Expand Down Expand Up @@ -384,6 +413,10 @@ impl MockJobBuilder {
self.dependencies.push(dependency);
}

pub fn git_info(&mut self, git_info: MockJobGitInfo) {
self.git_info = git_info;
}

pub fn build(self) -> MockJob {
assert!(!self.steps.is_empty(), "Should have at least one step");
let inner = MockJobInner {
Expand All @@ -403,6 +436,7 @@ impl MockJobBuilder {
variables: self.variables.into_values().collect(),
dependencies: self.dependencies,
artifacts: self.artifacts,
git_info: self.git_info,
inner,
}
}
Expand Down
3 changes: 2 additions & 1 deletion gitlab-runner-mock/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use wiremock::{Mock, MockServer};
mod job;
mod variables;
pub use job::{
MockJob, MockJobArtifactWhen, MockJobBuilder, MockJobState, MockJobStepName, MockJobStepWhen,
MockJob, MockJobArtifactWhen, MockJobBuilder, MockJobGitInfo, MockJobState, MockJobStepName,
MockJobStepWhen,
};

mod api;
Expand Down
4 changes: 4 additions & 0 deletions gitlab-runner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ hmac = "0.12.1"
rand = "0.10.0"
tokio-util = { version = "0.7.18", features = [ "io" ] }
tokio-retry2 = { version = "0.9.1", features = ["jitter"] }
gix = { version = "0.83.0", features = [ "blocking-http-transport-reqwest", "worktree-mutation" ] }
strum = { version = "0.28.0", features = ["derive"] }

[dev-dependencies]
tokio = { version = "1.50.0", features = [ "full", "test-util" ] }
Expand All @@ -44,6 +46,8 @@ gitlab-runner-mock = { path = "../gitlab-runner-mock" }
futures = "0.3.32"
anyhow = "1.0.102"
clap = { version = "4.6.0", features = ["derive"] }
assert_cmd = "2.2.1"
rstest = "0.26.1"

[build-dependencies]
vergen-gitcl = "9.1.0"
80 changes: 80 additions & 0 deletions gitlab-runner/examples/demo-runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,86 @@ impl Run {
}
Ok(())
}
"checkout" => {
let path = match p.next() {
Some(path) => self.job.build_dir().join(path),
_ => self.job.build_dir().to_path_buf(),
};
let Some(temp_repo_path) = self
.job
.clone_git_repository()
.await
.map_err(|e| outputln!("Failed to checkout repo: {}", e.to_string()))?
else {
outputln!("Git cloning is disabled, nothing to do");
return Ok(());
};

outputln!("Checked out to {:?}", temp_repo_path);

if !path.exists() {
std::fs::create_dir(&path).map_err(|e| {
outputln!("Failed to create dir for repo: {}", e.to_string())
})?;
}

let repo_read_dir = temp_repo_path.read_dir().map_err(|e| {
outputln!("Failed to enumerate repo dir: {}", e.to_string())
})?;
for entry in repo_read_dir {
let entry = entry.map_err(|e| {
outputln!("Failed to read repo file: {}", e.to_string())
})?;
std::fs::rename(entry.path(), path.join(entry.file_name())).map_err(
|e| outputln!("Failed to move repo file: {}", e.to_string()),
)?;
}

std::fs::remove_dir_all(temp_repo_path).map_err(|e| {
outputln!("Failed to remove temp directory: {}", e.to_string())
})?;

outputln!("Moved repo to {:?}", path);

Ok(())
}
"list-dir" => {
let path = match p.next() {
Some(path) => self.job.build_dir().join(path),
_ => self.job.build_dir().to_path_buf(),
};
if !path.exists() {
outputln!("Path {:?} doesn't exist", path);
} else {
for entry in path.read_dir().unwrap() {
match entry {
Ok(entry) => outputln!("{}", entry.file_name().to_str().unwrap()),
Err(e) => outputln!("Error: {:?}: {}", e, e.to_string()),
}
}
}
Ok(())
}
"cat" => {
let path = match p.next() {
Some(path) => self.job.build_dir().join(path),
_ => {
outputln!("No path provided");
return Err(());
}
};
if !path.exists() {
outputln!("Path {:?} doesn't exist", path);
Err(())
} else {
let data = match std::fs::read_to_string(path) {
Ok(s) => s,
_ => "Failed to read file to text".to_string(),
};
outputln!("{}", data);
Ok(())
}
}
_ => {
outputln!("Unknown command\n");
Err(())
Expand Down
67 changes: 67 additions & 0 deletions gitlab-runner/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,18 @@ pub(crate) struct JobDependency {
pub artifacts_file: Option<JobArtifactFile>,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub(crate) struct JobGitInfo {
pub repo_url: String,
pub refspecs: Vec<String>,
pub sha: String,
// Note that this is already filled with the value of GIT_DEPTH:
// https://gitlab.com/gitlab-org/gitlab/-/blob/dd9009f7d9f57fdfd7496f14086c0f059f890688/app/presenters/ci/build_runner_presenter.rb#L164
pub depth: u32,
#[serde(flatten)]
unparsed: JsonValue,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub(crate) struct JobResponse {
pub id: u64,
Expand All @@ -236,6 +248,7 @@ pub(crate) struct JobResponse {
pub dependencies: Vec<JobDependency>,
#[serde(deserialize_with = "deserialize_null_default")]
pub artifacts: Vec<JobArtifact>,
pub git_info: JobGitInfo,
#[serde(flatten)]
unparsed: JsonValue,
}
Expand All @@ -246,6 +259,60 @@ impl JobResponse {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumString)]
#[strum(
serialize_all = "lowercase",
parse_err_ty = GitCheckoutError,
parse_err_fn = GitStrategy::make_error,
)]
pub enum GitStrategy {
Clone,
Fetch,
None,
Empty,
}

impl GitStrategy {
fn make_error(s: &str) -> GitCheckoutError {
GitCheckoutErrorInner::BadGitStrategy(s.to_owned()).into()
}
}

#[derive(Error, Debug)]
pub enum GitCheckoutErrorInner {
#[error(transparent)]
GitClone(#[from] gix::clone::Error),
#[error(transparent)]
GitFetch(#[from] gix::clone::fetch::Error),
#[error(transparent)]
GitCheckout(#[from] gix::clone::checkout::main_worktree::Error),
#[error(transparent)]
GitRefEdit(#[from] gix::reference::edit::Error),
#[error(transparent)]
GitRefspecParse(#[from] gix::refspec::parse::Error),
#[error(transparent)]
GitHashDecode(#[from] gix::hash::decode::Error),
#[error(transparent)]
ThreadJoinError(#[from] tokio::task::JoinError),
#[error(transparent)]
Write(#[from] std::io::Error),
#[error("invalid GIT_STRATEGY: {0}")]
BadGitStrategy(String),
}

#[derive(Error, Debug)]
#[error(transparent)]
pub struct GitCheckoutError(pub Box<GitCheckoutErrorInner>);

impl<T> From<T> for GitCheckoutError
where
GitCheckoutErrorInner: From<T>,
{
fn from(value: T) -> Self {
Self(Box::new(value.into()))
}
}

#[derive(Error, Debug)]
pub enum Error {
#[error("Unexpected reply code {0}")]
Expand Down
Loading
Loading