Plumb Executor and workspace through Run::execute
Adds the parameters now so the per-run container lifecycle work
can wire up behavior without churning every call site again.
Assisted-by: Claude Opus 4.7 (1M context) via Claude Code
diff --git a/src/bin/quire/commands/ci.rs b/src/bin/quire/commands/ci.rs
index 8f7ee97..407525d 100644
--- a/src/bin/quire/commands/ci.rs
+++ b/src/bin/quire/commands/ci.rs
@@ -2,7 +2,7 @@ use std::path::PathBuf;
use miette::{IntoDiagnostic, Result};
use quire::Quire;
-use quire::ci::{Ci, CommitRef, RunMeta, Runs};
+use quire::ci::{Ci, CommitRef, Executor, RunMeta, Runs};
/// Validate a repo's ci.fnl without executing any jobs.
///
@@ -76,7 +76,15 @@ pub async fn run(quire: &Quire, maybe_sha: Option<&str>) -> Result<()> {
let run = runs.create(&meta)?;
println!("Run {}: executing at {}", run.id(), commit.display);
- let exec_result = run.execute(pipeline, secrets, &repo_path.join(".git"));
+ let workspace = tmp.path().join("workspace");
+ fs_err::create_dir_all(&workspace).into_diagnostic()?;
+ let exec_result = run.execute(
+ pipeline,
+ secrets,
+ &repo_path.join(".git"),
+ &workspace,
+ Executor::Host,
+ );
match exec_result {
Ok(outputs) => {
diff --git a/src/ci/mod.rs b/src/ci/mod.rs
index 0916015..bcaadd6 100644
--- a/src/ci/mod.rs
+++ b/src/ci/mod.rs
@@ -9,7 +9,7 @@ mod run;
mod runtime;
pub use pipeline::{DefinitionError, Diagnostic, Job, Pipeline, PipelineError, StructureError};
-pub use run::{Run, RunMeta, RunState, RunTimes, Runs};
+pub use run::{Executor, Run, RunMeta, RunState, RunTimes, Runs};
/// A resolved commit reference.
///
@@ -174,7 +174,15 @@ fn trigger_ref(
}
};
- run.execute(pipeline, secrets.clone(), &repo.path())?;
+ let workspace = run.path().join("workspace");
+ fs_err::create_dir_all(&workspace)?;
+ run.execute(
+ pipeline,
+ secrets.clone(),
+ &repo.path(),
+ &workspace,
+ run::Executor::Host,
+ )?;
Ok(())
}
diff --git a/src/ci/run.rs b/src/ci/run.rs
index 2513ced..a75dd94 100644
--- a/src/ci/run.rs
+++ b/src/ci/run.rs
@@ -18,6 +18,14 @@ use crate::display_chain;
use crate::secret::SecretString;
use crate::{Error, Result};
+/// The execution mode for a run. Host runs `sh` directly on the host.
+/// Docker materializes a container and routes `sh` through `docker exec`.
+#[derive(Debug, Clone)]
+pub enum Executor {
+ Host,
+ // Docker variant added in Task 5.
+}
+
/// The state of a CI run.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RunState {
@@ -274,7 +282,12 @@ impl Run {
pipeline: Pipeline,
secrets: HashMap<String, SecretString>,
git_dir: &std::path::Path,
+ workspace: &std::path::Path,
+ executor: Executor,
) -> Result<HashMap<String, Vec<ShOutput>>> {
+ // `workspace` and `executor` are not yet read; later tasks
+ // wire them into the runtime and per-run container lifecycle.
+ let _ = (workspace, executor);
let meta = self.read_meta()?;
let runtime = Rc::new(Runtime::new(pipeline, secrets, &meta, git_dir));
@@ -455,6 +468,14 @@ mod tests {
Runs::new(quire.base_dir().join("runs").join("test.git"))
}
+ /// Materialize a workspace directory under the test Quire's base dir.
+ /// Used by `Run::execute` call sites to satisfy the workspace param.
+ fn test_workspace(quire: &Quire) -> PathBuf {
+ let workspace = quire.base_dir().join("ws");
+ fs_err::create_dir_all(&workspace).expect("mkdir workspace");
+ workspace
+ }
+
fn test_meta() -> RunMeta {
RunMeta {
sha: "abc123".to_string(),
@@ -810,7 +831,13 @@ mod tests {
let run_id = run.id().to_string();
let outputs = run
- .execute(pipeline, HashMap::new(), std::path::Path::new("."))
+ .execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
.expect("execute");
// Verify the run landed in complete/ on disk.
@@ -843,8 +870,14 @@ mod tests {
);
let pipeline = load(&source);
- run.execute(pipeline, HashMap::new(), std::path::Path::new("."))
- .expect("execute");
+ run.execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
+ .expect("execute");
let contents = fs_err::read_to_string(&log).expect("read log");
assert_eq!(contents, "a\nb\n");
@@ -864,7 +897,13 @@ mod tests {
let run_id = run.id().to_string();
let err = run
- .execute(pipeline, HashMap::new(), std::path::Path::new("."))
+ .execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
.expect_err("expected failure");
assert!(matches!(err, Error::JobFailed { ref job, .. } if job == "a"));
@@ -888,7 +927,13 @@ mod tests {
);
let outputs = run
- .execute(pipeline, HashMap::new(), std::path::Path::new("."))
+ .execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
.expect("execute");
let grab = &outputs["grab"];
@@ -914,7 +959,13 @@ mod tests {
);
let outputs = run
- .execute(pipeline, HashMap::new(), std::path::Path::new("."))
+ .execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
.expect("execute");
let b = &outputs["b"];
@@ -934,7 +985,13 @@ mod tests {
);
let err = run
- .execute(pipeline, HashMap::new(), std::path::Path::new("."))
+ .execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
.expect_err("expected failure");
let Error::JobFailed { job, source } = err else {
unreachable!()
@@ -961,7 +1018,13 @@ mod tests {
);
let err = run
- .execute(pipeline, HashMap::new(), std::path::Path::new("."))
+ .execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
.expect_err("expected failure");
let Error::JobFailed { source, .. } = err else {
unreachable!()
@@ -985,7 +1048,13 @@ mod tests {
);
let err = run
- .execute(pipeline, HashMap::new(), std::path::Path::new("."))
+ .execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
.expect_err("expected failure");
let Error::JobFailed { source, .. } = err else {
unreachable!()
@@ -1014,7 +1083,13 @@ mod tests {
);
let outputs = run
- .execute(pipeline, HashMap::new(), std::path::Path::new("."))
+ .execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
.expect("execute");
let b = &outputs["b"];
assert_eq!(b.len(), 1);
@@ -1033,8 +1108,14 @@ mod tests {
);
let run_id = run.id().to_string();
- run.execute(pipeline, HashMap::new(), std::path::Path::new("."))
- .expect("execute");
+ run.execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
+ .expect("execute");
let log_path = runs
.base
@@ -1069,7 +1150,13 @@ mod tests {
);
let run_id = run.id().to_string();
- let _ = run.execute(pipeline, HashMap::new(), std::path::Path::new("."));
+ let _ = run.execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ );
let failed_dir = runs.base.join(RunState::Failed.dir_name()).join(&run_id);
assert!(failed_dir.exists(), "run should be in failed/");
@@ -1102,7 +1189,13 @@ mod tests {
);
let err = run
- .execute(pipeline, HashMap::new(), std::path::Path::new("."))
+ .execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
.expect_err("expected failure");
let Error::JobFailed { job, source } = err else {
panic!("expected JobFailed, got: {err:?}")
@@ -1135,8 +1228,14 @@ mod tests {
Ok(())
})));
- run.execute(pipeline, HashMap::new(), std::path::Path::new("."))
- .expect("execute should succeed");
+ run.execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
+ .expect("execute should succeed");
assert!(called.get(), "rust run-fn should have been called");
}
@@ -1156,7 +1255,13 @@ mod tests {
})));
let err = run
- .execute(pipeline, HashMap::new(), std::path::Path::new("."))
+ .execute(
+ pipeline,
+ HashMap::new(),
+ std::path::Path::new("."),
+ &test_workspace(&quire),
+ Executor::Host,
+ )
.expect_err("expected failure");
let Error::JobFailed { job, source } = err else {
panic!("expected JobFailed, got: {err:?}");