pub struct VersionPlan {Show 17 fields
pub policy: Policy,
pub baseline: Baseline,
pub declared_version: String,
pub tool_version: String,
pub writers_agree: bool,
pub atoms: usize,
pub fingerprint: String,
pub implied: Impact,
pub required: Impact,
pub declared: Impact,
pub required_version: String,
pub status: Status,
pub breaking: bool,
pub changes: Vec<SurfaceChange>,
pub commits: CommitEvidence,
pub understated: bool,
pub diagnostics: Vec<Diagnostic>,
}Expand description
The whole answer: what moved, what that requires, and what the tree declares.
One value, and every surface renders it — the command line, /api/v1/release/analysis,
the MCP tool, the Cockpit panel, the generated document and the CI gate. There is no
second computation anywhere, which is the property that makes the number mean something.
use majordomus_cli::release::compat::{Status, VersionPlan};
let plan = VersionPlan {
policy: Policy::for_version(Version::parse("0.5.0").unwrap()),
baseline: Baseline {
version: "0.5.0".into(), reference: "v0.5.0".into(), read_at: "3a032c1e".into(),
commit: "3a032c1e".into(), recorded: true, atoms: 300, fingerprint: "sha256:a".into(),
},
declared_version: "0.5.0".into(),
tool_version: "0.5.0".into(),
writers_agree: true,
atoms: 333,
fingerprint: "sha256:b".into(),
implied: Impact::Minor,
required: Impact::Minor,
declared: Impact::None,
required_version: "0.6.0".into(),
status: Status::Blocked,
breaking: false,
changes: Vec::new(),
commits: CommitEvidence { commits: 24, implied: Impact::Patch, breaking: Vec::new() },
understated: true,
diagnostics: Vec::new(),
};
// The contract requires a minor and the version declares none: this tree may not land.
assert_eq!(plan.status, Status::Blocked);
assert!(plan.declared < plan.required);
assert!(plan.understated, "the commit subjects claim less than the contract did");Fields§
§policy: PolicyThe policy the verdict was reached under.
baseline: BaselineThe release this was measured against.
declared_version: StringThe version the crate manifest declares — the one place it is authored.
tool_version: StringThe version bin/majordomus prints: what its projection, share/version.txt, states.
writers_agree: boolWhether that projection is current — states what the manifest declares. The name is kept from when the version had two writers; the manifest is the only one now.
atoms: usizeHow many public atoms this tree carries.
fingerprint: StringThe fingerprint of this tree’s surface.
implied: ImpactWhat the surface movement amounts to on its own.
required: ImpactWhat the policy requires of it.
declared: ImpactWhat the two version numbers already represent.
required_version: StringThe smallest version this tree may declare.
status: StatusWhether it does.
breaking: boolWhether anything a caller could hold is gone.
changes: Vec<SurfaceChange>Every movement of the contract, worst first.
commits: CommitEvidenceWhat the commits said about themselves.
understated: boolWhether the commit subjects understate what the contract actually did.
diagnostics: Vec<Diagnostic>Anything incoherent about the release state, whatever the verdict.
Implementations§
Source§impl VersionPlan
impl VersionPlan
Sourcepub fn measured_version(&self) -> Result<String, String>
pub fn measured_version(&self) -> Result<String, String>
The version the contract requires, when the analysis could measure one.
The analysis still returns a plan when a fact it rests on is untrustworthy — a version
that is not three numbers (baseline-version-malformed, declared-version-malformed,
then a guess), a baseline whose record and tag name different commits
(tag-commit-mismatch), a version stated by hand (writers-disagree,
version-stated-by-hand) — so a caller that takes required_version as the
contract’s answer must ask this instead. It is sound exactly when release bump would
write from it: any Severity::Error diagnostic (VersionPlan::has_errors) refuses,
with every error’s message, one per line, as bump prints them.
assert_eq!(plan("0.6.0", Vec::new()).measured_version(), Ok("0.6.0".into()));
// no version line in the manifest: the analysis echoes "", which is no answer
assert!(plan("", Vec::new()).measured_version().is_err());
let guessed = Diagnostic { id: "baseline-version-malformed".into(),
severity: Severity::Error, message: "the baseline is not three numbers".into() };
assert_eq!(plan("0.6.0", vec![guessed]).measured_version(),
Err("the baseline is not three numbers".into()));
// any error refuses, not only a malformed version: the plan bump would not write from
let moved = Diagnostic { id: "tag-commit-mismatch".into(),
severity: Severity::Error, message: "the tag moved".into() };
let stale = Diagnostic { id: "projection-stale".into(),
severity: Severity::Warning, message: "run scripts/derive".into() };
assert_eq!(plan("0.6.0", vec![stale.clone()]).measured_version(), Ok("0.6.0".into()));
let unsound = plan("0.6.0", vec![moved.clone(), stale, moved]);
assert!(unsound.has_errors());
assert_eq!(unsound.measured_version(), Err("the tag moved\nthe tag moved".into()));Sourcepub fn counts(&self) -> (usize, usize, usize)
pub fn counts(&self) -> (usize, usize, usize)
How many changes moved the contract in each direction.
let p = plan(vec![change(Direction::Added), change(Direction::Added), change(Direction::Removed)]);
assert_eq!(p.counts(), (2, 0, 1), "added, changed, removed");Sourcepub fn breaking_changes(&self) -> impl Iterator<Item = &SurfaceChange>
pub fn breaking_changes(&self) -> impl Iterator<Item = &SurfaceChange>
Everything that is breaking on its own.
assert_eq!(plan.breaking_changes().count(), 1, "only what is breaking on its own");Sourcepub fn has_errors(&self) -> bool
pub fn has_errors(&self) -> bool
Whether anything makes the plan itself untrustworthy.
assert!(!plan.has_errors());
// A warning leaves the plan standing; only an error makes it untrustworthy.
plan.diagnostics.push(Diagnostic { id: "tag-without-record".into(),
severity: Severity::Warning, message: "…".into() });
assert!(!plan.has_errors());
plan.diagnostics.push(Diagnostic { id: "writers-disagree".into(),
severity: Severity::Error, message: "…".into() });
assert!(plan.has_errors());Trait Implementations§
Source§impl Clone for VersionPlan
impl Clone for VersionPlan
Source§fn clone(&self) -> VersionPlan
fn clone(&self) -> VersionPlan
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for VersionPlan
impl Debug for VersionPlan
Source§impl<'de> Deserialize<'de> for VersionPlan
impl<'de> Deserialize<'de> for VersionPlan
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for VersionPlan
Source§impl JsonSchema for VersionPlan
impl JsonSchema for VersionPlan
Source§fn schema_id() -> Cow<'static, str>
fn schema_id() -> Cow<'static, str>
Source§fn json_schema(generator: &mut SchemaGenerator) -> Schema
fn json_schema(generator: &mut SchemaGenerator) -> Schema
Source§fn inline_schema() -> bool
fn inline_schema() -> bool
$ref keyword. Read moreSource§impl PartialEq for VersionPlan
impl PartialEq for VersionPlan
Source§fn eq(&self, other: &VersionPlan) -> bool
fn eq(&self, other: &VersionPlan) -> bool
self and other values to be equal, and is used by ==.Source§impl Serialize for VersionPlan
impl Serialize for VersionPlan
impl StructuralPartialEq for VersionPlan
Auto Trait Implementations§
impl Freeze for VersionPlan
impl RefUnwindSafe for VersionPlan
impl Send for VersionPlan
impl Sync for VersionPlan
impl Unpin for VersionPlan
impl UnsafeUnpin for VersionPlan
impl UnwindSafe for VersionPlan
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.