Move runtime CI modules into `quire-core`
`pipeline`, `registration`, `mirror`, `runtime`, and `RunMeta` land
in `quire-core::ci`. quire-server's `ci::mod.rs` re-exports the
modules so existing `super::pipeline::*` (etc.) imports keep working
across the boundary. The orchestrator-only pieces — `Run`, `Runs`,
`RunState`, `materialize_workspace`, `reconcile_orphans`, the
`error::Error` kitchen-sink — stay in `quire-server::ci`.
Visibility note: `pub(super)`/`pub(crate)` items used by
quire-server (`Pipeline`, `RunFn`, `Runtime`, `RuntimeHandle`,
`ShOutput`, `compile`, etc.) are widened to `pub` since they now
cross a crate boundary. `Pipeline::replace_first_run_fn` is
ungated from `#[cfg(test)]` and marked `#[doc(hidden)]` — a
quire-server test still needs it, and a cross-crate test-helper
feature flag would be heavier than the small unused method in
release.
Sets up the next step: `quire-ci`'s main can now read
`.quire/ci.fnl` and call `quire_core::ci::pipeline::compile` to
return a Pipeline.
Assisted-by: Claude Opus 4.7 (1M context) via Claude Code
diff --git a/Cargo.lock b/Cargo.lock
index 27aa9ac..e846408 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2135,8 +2135,10 @@ name = "quire-core"
version = "0.1.0"
dependencies = [
"fs-err",
+ "jiff",
"miette",
"mlua",
+ "petgraph",
"regex",
"serde",
"serde_json",
diff --git a/quire-core/Cargo.toml b/quire-core/Cargo.toml
index deb6125..8c50272 100644
--- a/quire-core/Cargo.toml
+++ b/quire-core/Cargo.toml
@@ -5,8 +5,10 @@ edition = "2024"
[dependencies]
fs-err = "*"
+jiff = { version = "*", features = ["serde"] }
miette = "*"
mlua = { version = "*", features = ["lua54", "serde", "vendored", "error-send"] }
+petgraph = "*"
regex = "*"
serde = { version = "*", features = ["derive"] }
thiserror = "*"
diff --git a/quire-server/src/ci/mirror.rs b/quire-core/src/ci/mirror.rs
similarity index 99%
rename from quire-server/src/ci/mirror.rs
rename to quire-core/src/ci/mirror.rs
index 265770c..fd5f27d 100644
--- a/quire-server/src/ci/mirror.rs
+++ b/quire-core/src/ci/mirror.rs
@@ -25,7 +25,7 @@ use super::runtime::{Cmd, Runtime, RuntimeError, RuntimeResult, ShOpts};
/// Closure state for the `quire/mirror` job's run-fn: everything the
/// tag-and-push needs at execute time, captured once at registration.
-pub(super) struct MirrorJob {
+pub struct MirrorJob {
url: String,
secret: String,
/// Refs to push to the remote. Empty means "push whatever ref
@@ -189,7 +189,7 @@ impl MirrorJob {
/// tag-and-push at execute time. Singleton-ness is enforced by
/// generic id uniqueness in `Registration::add_job` — a second
/// `(ci.mirror …)` collides on the `quire/mirror` id.
- pub(super) fn register(lua: &Lua, (url, opts): (String, mlua::Table)) -> mlua::Result<()> {
+ pub fn register(lua: &Lua, (url, opts): (String, mlua::Table)) -> mlua::Result<()> {
let r = lua.app_data_ref::<Registration>().ok_or_else(|| {
mlua::Error::external("quire.ci registration not installed on Lua VM")
})?;
@@ -236,7 +236,7 @@ mod tests {
use crate::ci::pipeline::{Diagnostic, RustRunFn, compile};
use crate::ci::run::RunMeta;
use crate::ci::runtime::RuntimeHandle;
- use quire_core::secret::{Error as SecretError, SecretString};
+ use crate::secret::{Error as SecretError, SecretString};
/// Set up a bare git repo with one commit. Returns the tempdir,
/// the bare repo path, and the head SHA.
diff --git a/quire-core/src/ci/mod.rs b/quire-core/src/ci/mod.rs
new file mode 100644
index 0000000..6d9f036
--- /dev/null
+++ b/quire-core/src/ci/mod.rs
@@ -0,0 +1,12 @@
+//! Compile-time and runtime CI primitives shared with `quire-ci`.
+//!
+//! The orchestrator lives in `quire-server::ci`; this module owns the
+//! pieces that need to run identically inside a per-run container
+//! (where `quire-ci` invokes them) and on the server (where the
+//! orchestrator drives them).
+
+pub mod mirror;
+pub mod pipeline;
+pub mod registration;
+pub mod run;
+pub mod runtime;
diff --git a/quire-server/src/ci/pipeline.rs b/quire-core/src/ci/pipeline.rs
similarity index 97%
rename from quire-server/src/ci/pipeline.rs
rename to quire-core/src/ci/pipeline.rs
index 899e01a..42b9971 100644
--- a/quire-server/src/ci/pipeline.rs
+++ b/quire-core/src/ci/pipeline.rs
@@ -12,7 +12,7 @@ use petgraph::graph::NodeIndex;
use petgraph::visit::{Bfs, Reversed};
use super::registration::{self, Registrations};
-use quire_core::fennel::{Fennel, FennelError};
+use crate::fennel::{Fennel, FennelError};
/// A registration-time error caught while individual `(ci.job …)` and
/// `(ci.image …)` calls are being processed.
@@ -104,14 +104,14 @@ pub struct Job {
pub inputs: Vec<String>,
/// Span covering the `(ci.job …)` call site. Used as the label
/// location for both per-job and post-graph diagnostics.
- pub(crate) span: SourceSpan,
+ pub span: SourceSpan,
/// What to run when the executor reaches this job.
- pub(super) run_fn: RunFn,
+ pub run_fn: RunFn,
}
/// A Rust-side run-fn: a closure invoked synchronously by the
/// executor with the runtime in scope.
-pub(super) type RustRunFn =
+pub type RustRunFn =
std::rc::Rc<dyn Fn(&super::runtime::Runtime) -> super::runtime::RuntimeResult<()>>;
/// How a job runs at execute time.
@@ -126,7 +126,7 @@ pub(super) type RustRunFn =
/// before invoking — `mlua::Function` is cheap to clone (a registry
/// handle); the `Rc` makes the `Rust` variant cheap too.
#[derive(Clone)]
-pub(super) enum RunFn {
+pub enum RunFn {
Lua(mlua::Function),
#[allow(dead_code)] // Wired up by `(ci.mirror …)` and friends.
Rust(RustRunFn),
@@ -155,7 +155,7 @@ impl Job {
///
/// Visible to the sibling `registration` module which constructs
/// jobs from the registration callbacks.
- pub(super) fn new(
+ pub fn new(
id: String,
inputs: Vec<String>,
run_fn: RunFn,
@@ -209,7 +209,7 @@ impl Pipeline {
}
/// Borrow the underlying Fennel/Lua VM.
- pub(crate) fn fennel(&self) -> &Fennel {
+ pub fn fennel(&self) -> &Fennel {
&self.fennel
}
@@ -223,7 +223,7 @@ impl Pipeline {
/// Return job IDs in topological order — dependencies before
/// dependents. The pipeline is already validated as acyclic, so
/// this never fails.
- pub(crate) fn topo_order(&self) -> Vec<&str> {
+ pub fn topo_order(&self) -> Vec<&str> {
petgraph::algo::toposort(&self.graph, None)
.expect("pipeline is validated as acyclic")
.into_iter()
@@ -242,7 +242,7 @@ impl Pipeline {
/// the job) via petgraph's BFS. Source refs aren't graph nodes,
/// so they're scooped up from the inputs lists of every visited
/// job.
- pub(crate) fn transitive_inputs(&self) -> HashMap<String, HashSet<String>> {
+ pub fn transitive_inputs(&self) -> HashMap<String, HashSet<String>> {
let reversed = Reversed(&self.graph);
let mut result: HashMap<String, HashSet<String>> = HashMap::new();
for job in &self.jobs {
@@ -266,12 +266,12 @@ impl Pipeline {
}
}
-#[cfg(test)]
impl Pipeline {
/// Replace the first job's run-fn — for tests that need to
/// exercise a `RunFn::Rust` execution path without building the
/// full helper machinery (which doesn't exist yet).
- pub(super) fn replace_first_run_fn(&mut self, run_fn: RunFn) {
+ #[doc(hidden)]
+ pub fn replace_first_run_fn(&mut self, run_fn: RunFn) {
if let Some(job) = self.jobs.first_mut() {
job.run_fn = run_fn;
}
@@ -301,7 +301,7 @@ fn build_graph(jobs: &[Job]) -> (JobGraph, HashMap<String, NodeIndex>) {
/// Compute a span covering the given 1-indexed line in `source`.
/// Returns an empty span at offset 0 when the line is unknown.
-pub(super) fn span_for_line(source: &str, line: u32) -> SourceSpan {
+pub fn span_for_line(source: &str, line: u32) -> SourceSpan {
if line == 0 {
return SourceSpan::from((0, 0)); // cov-excl-line
}
@@ -330,10 +330,10 @@ pub struct PipelineError {
// Named `src` rather than `source` so thiserror doesn't auto-treat
// it as the error chain.
#[source_code]
- pub(crate) src: NamedSource<String>,
+ pub src: NamedSource<String>,
#[related]
- pub(crate) diagnostics: Vec<Diagnostic>,
+ pub diagnostics: Vec<Diagnostic>,
}
/// Errors from [`compile`] — Fennel evaluation failures and pipeline-shape
@@ -372,7 +372,7 @@ pub type CompileResult<T> = std::result::Result<T, CompileError>;
/// [`validate_post_graph`] checks the dependency graph. Errors from a
/// phase are wrapped in a [`PipelineError`] for miette to render with
/// inline labels.
-pub(crate) fn compile(source: &str, name: &str) -> CompileResult<Pipeline> {
+pub fn compile(source: &str, name: &str) -> CompileResult<Pipeline> {
let fennel = Fennel::new()?;
let Registrations { jobs, image } = registration::register(&fennel, source, name)?;
diff --git a/quire-server/src/ci/registration.rs b/quire-core/src/ci/registration.rs
similarity index 93%
rename from quire-server/src/ci/registration.rs
rename to quire-core/src/ci/registration.rs
index 2e06228..d973f62 100644
--- a/quire-server/src/ci/registration.rs
+++ b/quire-core/src/ci/registration.rs
@@ -18,15 +18,15 @@ use super::mirror;
use super::pipeline::{
self, CompileResult, DefinitionError, Diagnostic, Job, PipelineError, RunFn,
};
-use quire_core::fennel::Fennel;
+use crate::fennel::Fennel;
/// Output of [`register`]: jobs and image successfully registered
/// from the script. Definition-time errors are returned via the `Err`
/// arm, not collected here.
#[derive(Debug)]
-pub(super) struct Registrations {
- pub(super) jobs: Vec<Job>,
- pub(super) image: Option<String>,
+pub struct Registrations {
+ pub jobs: Vec<Job>,
+ pub image: Option<String>,
}
/// Evaluate `source` with the registration module bound and collect
@@ -36,7 +36,7 @@ pub(super) struct Registrations {
/// not abort the rest of the script — but if any rule fired, the
/// whole batch is returned as a `PipelineError` instead of partial
/// registrations.
-pub(super) fn register(fennel: &Fennel, source: &str, name: &str) -> CompileResult<Registrations> {
+pub fn register(fennel: &Fennel, source: &str, name: &str) -> CompileResult<Registrations> {
let jobs: Rc<RefCell<Vec<Job>>> = Rc::new(RefCell::new(Vec::new()));
let image = Rc::new(RefCell::new(None));
let src = Rc::new(source.to_string());
@@ -91,11 +91,11 @@ pub(super) fn register(fennel: &Fennel, source: &str, name: &str) -> CompileResu
/// (fn [{: sh : secret}]
/// (sh ["echo" (secret :github_token)])))
/// ```
-pub(super) struct Registration {
- pub(super) jobs: Rc<RefCell<Vec<Job>>>,
- pub(super) errors: Rc<RefCell<Vec<DefinitionError>>>,
+pub struct Registration {
+ pub jobs: Rc<RefCell<Vec<Job>>>,
+ pub errors: Rc<RefCell<Vec<DefinitionError>>>,
image: Rc<RefCell<Option<ImageRegistration>>>,
- pub(super) source: Rc<String>,
+ pub source: Rc<String>,
}
impl IntoLua for Registration {
@@ -113,7 +113,7 @@ impl Registration {
/// Push a registered job after enforcing id uniqueness. On
/// collision, records `DuplicateJob` against the caller's source
/// line and drops the new job; the first registration wins.
- pub(super) fn add_job(&self, job: Job, line: u32) {
+ pub fn add_job(&self, job: Job, line: u32) {
let mut jobs = self.jobs.borrow_mut();
if jobs.iter().any(|j| j.id == job.id) {
let span = pipeline::span_for_line(&self.source, line);
diff --git a/quire-core/src/ci/run.rs b/quire-core/src/ci/run.rs
new file mode 100644
index 0000000..b6ba4db
--- /dev/null
+++ b/quire-core/src/ci/run.rs
@@ -0,0 +1,20 @@
+//! Run-level data shared between the orchestrator and the runtime.
+//!
+//! The full `Run` lifecycle (state machine, db rows, log files) lives
+//! in `quire-server::ci::run`. This module carries only the immutable
+//! metadata that the runtime needs at execute time so it can run in
+//! either a server or an in-container `quire-ci` context.
+
+use jiff::Timestamp;
+
+/// Immutable metadata for a CI run. Passed to `Runs::create` at
+/// enqueue time; the fields are written to the `runs` row once.
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+pub struct RunMeta {
+ /// The commit SHA that triggered this run.
+ pub sha: String,
+ /// The full ref name (e.g. `refs/heads/main`).
+ pub r#ref: String,
+ /// When the push occurred.
+ pub pushed_at: Timestamp,
+}
diff --git a/quire-server/src/ci/runtime.rs b/quire-core/src/ci/runtime.rs
similarity index 95%
rename from quire-server/src/ci/runtime.rs
rename to quire-core/src/ci/runtime.rs
index ab8ef79..fed40f2 100644
--- a/quire-server/src/ci/runtime.rs
+++ b/quire-core/src/ci/runtime.rs
@@ -15,7 +15,7 @@ use mlua::{IntoLua, Lua, LuaSerdeExt};
use super::pipeline::{Job, Pipeline};
use super::run::RunMeta;
-use quire_core::secret::{self, SecretRegistry, SecretString, redact};
+use crate::secret::{self, SecretRegistry, SecretString, redact};
/// Errors produced by [`Runtime`] methods and the `RunFn::Rust`
/// callbacks that hold them. A small sum carved out of the
@@ -50,7 +50,7 @@ impl From<mlua::Error> for RuntimeError {
pub type RuntimeResult<T> = std::result::Result<T, RuntimeError>;
/// Per-sh timing: (index, started_at, finished_at).
-pub(super) type ShTimings = Vec<(usize, Timestamp, Timestamp)>;
+pub type ShTimings = Vec<(usize, Timestamp, Timestamp)>;
/// Per-execution runtime: owns the Lua VM, holds the secrets exposed
/// to the job, the per-job `(jobs name)` views, the current-job
@@ -73,18 +73,18 @@ pub(super) type ShTimings = Vec<(usize, Timestamp, Timestamp)>;
/// All three handle primitives — `(sh …)`, `(secret …)`, and
/// `(jobs …)` — require a runtime to be installed on the VM. Calls
/// from a VM without one error.
-pub(super) struct Runtime {
+pub struct Runtime {
pipeline: Pipeline,
/// Unified secret store: holds declared secrets and their revealed
/// values for both lookup and redaction. No Debug impl on the
/// registry; Runtime must not derive Debug either.
- pub(super) registry: RefCell<SecretRegistry>,
- pub(super) inputs: HashMap<String, HashMap<String, Option<mlua::Value>>>,
- pub(super) current_job: RefCell<Option<String>>,
- pub(super) outputs: RefCell<HashMap<String, Vec<ShOutput>>>,
+ pub registry: RefCell<SecretRegistry>,
+ pub inputs: HashMap<String, HashMap<String, Option<mlua::Value>>>,
+ pub current_job: RefCell<Option<String>>,
+ pub outputs: RefCell<HashMap<String, Vec<ShOutput>>>,
/// Per-sh timing records: job_id → (sh_index, started_at, finished_at).
/// Parallel to `outputs`; each entry at the same index corresponds.
- pub(super) sh_timings: RefCell<HashMap<String, ShTimings>>,
+ pub sh_timings: RefCell<HashMap<String, ShTimings>>,
/// Per-job sh call counter for assigning sequential indices.
sh_counter: RefCell<HashMap<String, usize>>,
/// The materialized workspace for this run. Every `(sh …)` call
@@ -104,7 +104,7 @@ impl Runtime {
/// failure — abort is the right answer there. The matching
/// `RuntimeHandle::into_lua` call at the executor's call site uses
/// the same boundary.
- pub(super) fn new(
+ pub fn new(
pipeline: Pipeline,
secrets: HashMap<String, SecretString>,
meta: &RunMeta,
@@ -155,17 +155,17 @@ impl Runtime {
}
/// Borrow the underlying Lua VM.
- pub(super) fn lua(&self) -> &Lua {
+ pub fn lua(&self) -> &Lua {
self.pipeline.fennel().lua()
}
/// The topo-sorted job IDs in execution order.
- pub(super) fn topo_order(&self) -> Vec<&str> {
+ pub fn topo_order(&self) -> Vec<&str> {
self.pipeline.topo_order()
}
/// Look up a job by id.
- pub(super) fn job(&self, id: &str) -> Option<&Job> {
+ pub fn job(&self, id: &str) -> Option<&Job> {
self.pipeline.job(id)
}
@@ -176,7 +176,7 @@ impl Runtime {
/// Panics if `id` has no inputs view — every job built by
/// `Runtime::new` gets one, so a missing view means the executor
/// is calling `enter_job` with an id that wasn't in the pipeline.
- pub(super) fn enter_job(&self, id: &str) {
+ pub fn enter_job(&self, id: &str) {
assert!(
self.inputs.contains_key(id),
"enter_job called with unknown job id '{id}'"
@@ -186,17 +186,17 @@ impl Runtime {
/// Clear the current-job cursor. Subsequent `(sh …)` calls (if
/// any) won't be attributed to a job until `enter_job` is called again.
- pub(super) fn leave_job(&self) {
+ pub fn leave_job(&self) {
*self.current_job.borrow_mut() = None;
}
/// Drain all recorded outputs, returning them keyed by job id.
- pub(super) fn take_outputs(&self) -> HashMap<String, Vec<ShOutput>> {
+ pub fn take_outputs(&self) -> HashMap<String, Vec<ShOutput>> {
std::mem::take(&mut *self.outputs.borrow_mut())
}
/// Drain all recorded sh timings, returning them keyed by job id.
- pub(super) fn take_sh_timings(&self) -> HashMap<String, ShTimings> {
+ pub fn take_sh_timings(&self) -> HashMap<String, ShTimings> {
std::mem::take(&mut *self.sh_timings.borrow_mut())
}
@@ -209,14 +209,14 @@ impl Runtime {
/// the full caveat.
///
/// [`SecretRegistry::resolve`]: quire_core::secret::SecretRegistry::resolve
- pub(super) fn secret(&self, name: &str) -> RuntimeResult<String> {
+ pub fn secret(&self, name: &str) -> RuntimeResult<String> {
self.registry.borrow_mut().resolve(name).map_err(Into::into)
}
/// Run `cmd` with `opts` and record its output against the
/// current job (if one is active). Non-zero exits come back in
/// `:exit`, not as `Err`.
- pub(super) fn sh(&self, cmd: Cmd, opts: ShOpts) -> RuntimeResult<ShOutput> {
+ pub fn sh(&self, cmd: Cmd, opts: ShOpts) -> RuntimeResult<ShOutput> {
let started_at = Timestamp::now();
let program = cmd.program().to_string();
let output =
@@ -279,7 +279,7 @@ impl Runtime {
/// `IntoLua` carrier for an `Rc<Runtime>`. Stows the Rc on the VM as
/// app data and returns the handle table — `{sh, secret, jobs}`.
-pub(super) struct RuntimeHandle(pub Rc<Runtime>);
+pub struct RuntimeHandle(pub Rc<Runtime>);
impl IntoLua for RuntimeHandle {
// Errors raised by the closures below cross the mlua boundary via
@@ -362,7 +362,7 @@ impl IntoLua for RuntimeHandle {
/// `From<Cmd> for Command` can't be handed an empty argv. The
/// non-empty invariant is enforced in [`mlua::FromLua`] before this
/// type is ever built.
-pub(super) enum Cmd {
+pub enum Cmd {
Shell(String),
Argv { program: String, args: Vec<String> },
}
@@ -421,7 +421,7 @@ impl Cmd {
// Also revisit the `from_utf8_lossy` calls below — non-UTF-8 bytes
// are silently replaced with U+FFFD and `:stdout` / `:stderr` end
// up as mojibake with no signal that anything was lost.
- pub(super) fn run(self, opts: ShOpts, cwd: &std::path::Path) -> std::io::Result<ShOutput> {
+ pub fn run(self, opts: ShOpts, cwd: &std::path::Path) -> std::io::Result<ShOutput> {
let cmd_str = format!("{self}");
let mut command: std::process::Command = self.into();
for (k, v) in opts.env {
@@ -478,8 +478,8 @@ impl mlua::FromLua for Cmd {
/// closed so typos surface rather than being silently ignored.
#[derive(Clone, Default, serde::Deserialize)]
#[serde(default, deny_unknown_fields)]
-pub(super) struct ShOpts {
- pub(super) env: HashMap<String, String>,
+pub struct ShOpts {
+ pub env: HashMap<String, String>,
}
impl mlua::FromLua for ShOpts {
diff --git a/quire-core/src/lib.rs b/quire-core/src/lib.rs
index 9d2811f..f3f8311 100644
--- a/quire-core/src/lib.rs
+++ b/quire-core/src/lib.rs
@@ -1,5 +1,6 @@
//! Shared runtime modules for the quire orchestrator (`quire-server`)
//! and the in-container runner (`quire-ci`).
+pub mod ci;
pub mod fennel;
pub mod secret;
diff --git a/quire-server/src/ci/mod.rs b/quire-server/src/ci/mod.rs
index cf8f36b..14abe06 100644
--- a/quire-server/src/ci/mod.rs
+++ b/quire-server/src/ci/mod.rs
@@ -3,17 +3,17 @@
use std::collections::HashMap;
pub(crate) mod logs;
-mod mirror;
-mod pipeline;
-mod registration;
mod run;
-mod runtime;
pub(crate) mod error;
pub use error::{Error, Result};
-pub use pipeline::{DefinitionError, Diagnostic, Job, Pipeline, PipelineError, StructureError};
-pub use run::{Run, RunMeta, RunState, Runs, materialize_workspace, reconcile_orphans};
+pub use quire_core::ci::pipeline::{
+ DefinitionError, Diagnostic, Job, Pipeline, PipelineError, StructureError,
+};
+pub use quire_core::ci::run::RunMeta;
+pub use quire_core::ci::{mirror, pipeline, registration, runtime};
+pub use run::{Run, RunState, Runs, materialize_workspace, reconcile_orphans};
/// A resolved commit reference.
///
diff --git a/quire-server/src/ci/run.rs b/quire-server/src/ci/run.rs
index 19bc899..e7562e4 100644
--- a/quire-server/src/ci/run.rs
+++ b/quire-server/src/ci/run.rs
@@ -13,10 +13,12 @@ use jiff::Timestamp;
use mlua::IntoLua;
use super::error::{Error, Result};
-use super::pipeline::{Pipeline, RunFn};
-use super::runtime::{Runtime, RuntimeHandle, ShOutput};
+use quire_core::ci::pipeline::{Pipeline, RunFn};
+use quire_core::ci::runtime::{Runtime, RuntimeHandle, ShOutput};
use quire_core::secret::SecretString;
+pub use quire_core::ci::run::RunMeta;
+
/// The state of a CI run.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RunState {
@@ -55,18 +57,6 @@ impl std::str::FromStr for RunState {
}
}
-/// Immutable metadata for a CI run. Passed to `Runs::create` at
-/// enqueue time; the fields are written to the `runs` row once.
-#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
-pub struct RunMeta {
- /// The commit SHA that triggered this run.
- pub sha: String,
- /// The full ref name (e.g. `refs/heads/main`).
- pub r#ref: String,
- /// When the push occurred.
- pub pushed_at: Timestamp,
-}
-
/// Access to CI runs for a single repo.
///
/// Owns the database path, repo name, and base directory for run
@@ -315,7 +305,7 @@ impl Run {
fn write_sh_records(
&self,
outputs: &HashMap<String, Vec<ShOutput>>,
- timings: &HashMap<String, super::runtime::ShTimings>,
+ timings: &HashMap<String, quire_core::ci::runtime::ShTimings>,
) -> Result<()> {
if outputs.is_empty() {
return Ok(());
@@ -826,7 +816,7 @@ mod tests {
}
fn load(source: &str) -> Pipeline {
- super::super::pipeline::compile(source, "ci.fnl").expect("compile should succeed")
+ quire_core::ci::pipeline::compile(source, "ci.fnl").expect("compile should succeed")
}
#[test]