Skip to main content

QedStep

Struct QedStep 

Source
pub struct QedStep {
Show 32 fields pub name: String, pub argv: Vec<String>, pub cwd: Option<String>, pub env: HashMap<String, String>, pub timeout: Option<u64>, pub on_fail: OnFail, pub produces: Vec<ProducedArtifact>, pub runtime: Option<TaskRuntime>, pub kind: StepKind, pub image: Option<String>, pub tag: Option<String>, pub push: bool, pub platforms: Vec<String>, pub binary_path: Option<String>, pub triple: Option<String>, pub package: Option<String>, pub context: Option<PathBuf>, pub load: bool, pub sub_pipeline: Option<SubPipelineConfig>, pub outputs: Vec<OutputDecl>, pub gha_workflow: Option<GhaWorkflowConfig>, pub import: Option<ImportConfig>, pub matrix: Option<MatrixSpec>, pub enabled: bool, pub activation: StepActivation, pub if_cond: Option<String>, pub background: bool, pub background_until: Option<String>, pub wait_for: Option<WaitForConfig>, pub manifest_stitch: Option<ManifestStitchConfig>, pub platform: Option<PlatformSpec>, pub toolchain: Option<ToolchainSpec>,
}

Fields§

§name: String§argv: Vec<String>§cwd: Option<String>§env: HashMap<String, String>§timeout: Option<u64>

Per-step budget in seconds (R603-B6). Every pipeline TOML has always written seconds (timeout = 1800 for a 30-minute cargo check, timeout = 9000 for the 2.5h rusty-v8 build), but the runner used to lower this with Millis::from_ms, reading 9000 as 9 milliseconds-worth of seconds — i.e. 9s. That stayed invisible for local steps (the local driver never enforces spec.timeout) and silently killed every long REMOTE step at 1/1000th of its budget. Lower it with [Millis::from_secs], never from_ms.

§on_fail: OnFail§produces: Vec<ProducedArtifact>

Release artifacts this step builds, declared so an Outcome::Publish can collect + upload them into the R2 release channel (R330-F3). Only the artifacts of successful steps are collected. Defaults to empty — most steps (check, typecheck) produce nothing publishable.

§runtime: Option<TaskRuntime>

How this step is sandboxed. None defers to the pipeline default (resolved from --where: local ⇒ Native, remote ⇒ Container). Setting it explicitly in TOML pins the runtime regardless of where the pipeline runs — used by build-image steps that must always be containerised.

§kind: StepKind

Which step variant this is. Defaults to StepKind::Subprocess — existing TOML and Rust literals omit the field. Set to StepKind::BuildImage to build an image instead of running argv.

§image: Option<String>

Catalog entry name resolved by the image catalog loader (R381-T1). Required when kind = build-image; used by Subprocess only as a nominal hint until the per-step image-override path is wired through (R381 follow-up — see runner notes).

§tag: Option<String>

Output tag when kind = build-image. Defaults to the step’s name.

§push: bool

When kind = build-image, push the resulting image to its registry after a successful build. Ignored for other kinds.

§platforms: Vec<String>

For kind = build-image: docker --platform values the image is built for (e.g. ["linux/amd64"]). Empty (the default) means host-native — buildx picks the daemon’s own platform, which is what every pre-existing build-image step got.

This is the image platform, distinct from platform.target (the Rust triple a build produces). A foreign-arch entry here does NOT authorize emulation: the runner refuses to build a foreign platform on a local docker daemon (that is QEMU by another name) unless the step also declares platform = { native = true, … }, which routes it to an arch-matched build-worker instead.

§binary_path: Option<String>

For kind = package-native-tarball (R407-T2): filesystem path to the static musl Rust binary produced by an earlier build step. Resolved relative to the camp root.

§triple: Option<String>

For kind = package-native-tarball (R407-T2): target-triple shorthand (e.g. x86_64-unknown-linux-musl) baked into the tarball stem and the emitted manifest. None resolves to the build host’s triple at packaging time.

§package: Option<String>

For kind = musl-static-preflight (R407-T3): workspace member name to gate (e.g. yubaba, yah). The runner walks its transitive dep closure and fails if any crate in crate::preflight::KNOWN_GLIBC_ONLY_CRATES appears.

§context: Option<PathBuf>

For kind = build-image: docker build context directory, resolved relative to the camp root. Defaults to . (camp root itself) when absent — the same behaviour as before this field existed. Use this to point at a staging directory assembled by an earlier subprocess step (e.g. context = "target/yah-yubaba-ctx").

§load: bool

For kind = build-image: load the finished image into the local docker daemon with --load instead of writing an OCI archive. Use in dev pipelines where the image must be immediately runnable. Mutually exclusive with multi-platform builds; ignored when push = true.

§sub_pipeline: Option<SubPipelineConfig>

For kind = sub-pipeline (W201-F1): the target to resolve as a child pipeline, params to forward, and what to roll up into the parent. The runner recurses into the resolved child as a nested QedRun parented to the caller; ProducedArtifacts and named outputs flow back per SubPipelineCollect.

§outputs: Vec<OutputDecl>

Named outputs this step may emit (W201-F4). Subprocess steps write KEY=VALUE\n lines to $YAH_OUTPUTS; the runner captures them in StepStatus::outputs for downstream sibling substitution via ${{ steps.<name>.outputs.<key> }}. Declaring outputs here is advisory — undeclared keys are captured too.

§gha_workflow: Option<GhaWorkflowConfig>

For kind = gha-workflow (W200-F9): path to a .github/workflows/*.yml, with optional event + dispatch inputs. Resolved relative to the camp root. Required when kind = gha-workflow; validate() rejects misconfiguration at parse time the same way sub_pipeline does.

§import: Option<ImportConfig>

For kind = import (W224, R533-F1): the imported workflow.yml source, its pinned blake3 content hash, and the virtual/materialize toggle. Required when kind = import; validate() rejects misconfiguration at parse time the same way gha_workflow / sub_pipeline do. The runner re-reads the source, recomputes its hash, and expands it into the native subgraph at plan time (crate::import).

§matrix: Option<MatrixSpec>

Step-level matrix (R505). When present, crate::matrix::plan fans this single step out into N step instances within the parent job, each carrying its row’s coord substituted into argv / env / cwd and its name suffixed with the coord pairs.

§enabled: bool

Declarative on/off switch (R506). When false, the runner skips the step at plan-time: a StepStatus with RunStatus::Skipped is still emitted so the dashboard renders the row, but no subprocess / container / sub-pipeline is launched. Defaults to true. Orthogonal to Self::activation — enabled = false means “I explicitly want this off for this run”; status = "stubbed" means “this is a planned but not-yet-implemented surface”. The runner treats both as skip; the dashboard renders them distinctly.

§activation: StepActivation

Declarative lifecycle state (R506). active (the default) runs the step normally; stubbed marks the step as a visible-but-skipped row — typically a planned target (e.g. ios-device, rpi0) that hasn’t been wired up yet but should still appear in the dashboard so bit-rot is observable. The runner skips stubbed steps the same way it skips enabled = false steps; the on-demand --include-stubbed override runs them.

§if_cond: Option<String>

Runtime conditional (R506) — a ${{ <expr> }}-style expression evaluated against the W201-F4 context (matrix coords, env, prior steps.<X>.outputs.<Y>, plus success() / failure()). When the expression evaluates to a falsy value the step is skipped at dispatch-time with RunStatus::Skipped. Bare expressions without ${{ }} delimiters are evaluated as implicit-expression bodies (GHA semantics). Layered above Self::enabled / Self::activation: a step that is enabled = false is skipped before if is consulted. Layered above Self::on_fail: this gate is pre-execution, while on_fail is post-failure propagation.

§background: bool

Run this step as a long-lived sidecar (R513-F2, W207 Gap #4). A background step is spawned — run() emits its StepStarted, kicks the subprocess onto its own task, and immediately advances to the next step instead of awaiting completion. The classic case is a server a later step talks to: yah-camp, vite preview, a mock auth broker. Without this every such step would block the pipeline forever.

Lifecycle: the sidecar lives until it is reaped. With Self::background_until unset it is reaped at the end of the step loop (after the last foreground step, before terminal outcomes); with background_until = "<step>" it is reaped the moment that named step finishes. Reaping a still-running sidecar kills it (kill_on_drop) and records RunStatus::Success — a healthy server torn down on schedule is the expected path, not a failure. A sidecar that exits on its own before reap surfaces its real exit status: clean → Success, non-zero → Failed (a sidecar that crashes mid-pipeline is a genuine problem and flips the run to Failed so on_fail fires).

Log story: a background step’s stdout/stderr keep streaming as StepOutput events tagged with the step index, identical to a foreground step — a misbehaving sidecar’s logs are exactly what you want when triaging, so v1 never silences them; collapsing a chatty sidecar’s pane is a consumer concern.

v1 scope: background is only valid on StepKind::Subprocess steps run locally (the [crate::ForgeExecutor] spawn path). validate() rejects other kinds; the runner rejects --where=remote background steps (yubaba-supervised remote sidecars are a separate lifecycle). Defaults to false — omitted from every existing pipeline.

§background_until: Option<String>

Reap this background step right after the named step finishes, rather than at the end of the pipeline (R513-F2). Implies Self::background. The named step must appear after this one in the pipeline — the runner rejects a forward-reference to a missing or earlier step at run start, so a typo fails loudly instead of silently deferring the reap to pipeline end. None (the default) ⇒ reap at end of the step loop.

§wait_for: Option<WaitForConfig>

For kind = wait-for (R513-F3, W207 Gap #5): the network endpoint to poll and the timeout/interval budget. Required when kind = wait-for; validate() rejects misconfiguration (missing block, no target, both targets) at parse time the same way sub_pipeline / gha_workflow do. None for every other step kind.

§manifest_stitch: Option<ManifestStitchConfig>

For kind = manifest-stitch (R590-F2): the arch-agnostic target tag and the per-arch source tags to fold into a multi-arch manifest list. Required when kind = manifest-stitch; validate() rejects a missing block / empty target / no sources at parse time the same way wait_for does. None for every other step kind.

§platform: Option<PlatformSpec>

Structured platform intent (R531-F2, W222): what target this step produces and the arch of the base image it pulls. host is not declared here — it’s self-detected per runner (R531-T1) and composed in at plan time via crate::platform::Platform::compose. None (the default, omitted from every existing pipeline file) means host-native / no foreign-arch container — the common case. An explicit platform.target overrides the legacy per-kind triple field as the composed target.

§toolchain: Option<ToolchainSpec>

Per-step toolchain pin overrides (R507, W208 pillar 3). An inline table toolchain.<tool> = "..." whose entries crate::toolchain::effective_pins overlays on the pipeline-level [pipeline.toolchain] — so a single build-android step can pin ndk = "r26d" while the pipeline pins r27. None (the default) ⇒ the step inherits the pipeline pins unchanged. See crate::toolchain.

Implementations§

Source§

impl QedStep

Source

pub fn is_background(&self) -> bool

true when this step runs as a long-lived sidecar (R513-F2) — either background = true or a background_until target is set. See Self::background for the lifecycle.

Source

pub fn validate(&self) -> Result<(), StepValidationError>

Validate kind-specific invariants. Called by the TOML loader (PipelineLoader::load_from_file) before the pipeline reaches the runner — fail loudly at parse time, not at execution time.

Source

pub fn validate_finally(&self) -> Result<(), StepValidationError>

Validate a step that lives in a pipeline’s [[finally]] teardown block (R513-F4). Runs the normal kind-specific Self::validate first, then enforces the v1 finally-only constraint: teardown is a plain StepKind::Subprocess and never a background sidecar (a detached teardown step has no one to reap it). Composite / image / sub-pipeline teardown is a documented follow-up.

Trait Implementations§

Source§

impl Clone for QedStep

Source§

fn clone(&self) -> QedStep

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for QedStep

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for QedStep

Deliberately not #[derive(Default)].

enabled carries #[serde(default = "default_enabled")] = true, and a derived Default would give it false — so QedStep { name, argv, ..Default::default() } would build a step that the runner silently skips, and a pipeline made only of such steps reports Success having run nothing. (R633 hit exactly that: a synthesized image build “succeeded” in 40 ms.)

Round-tripping serde’s own defaults makes the two definitions the same definition, so a future #[serde(default = …)] on some other field cannot reintroduce the divergence. name is the only field without a serde default.

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for QedStep

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for QedStep

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Allocation for T
where T: RefUnwindSafe + Send + Sync,

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Converts Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Converts &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Converts &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSend for T
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send> ⓘ

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Sync + Send>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send> ⓘ

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> Fruit for T
where T: Send + Downcast,

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Serialize for T
where T: Serialize + ?Sized,

Source§

fn erased_serialize(&self, serializer: &mut dyn Serializer) -> Result<(), Error>

Source§

fn do_erased_serialize( &self, serializer: &mut dyn Serializer, ) -> Result<(), ErrorImpl>

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more