1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//! Compile-time build provenance: the crate version plus the git commit the
//! binary was built from and that commit's date.
//!
//! `--version` and `GET /health` surface these so an operator can tell exactly
//! which build is running — not just the crate version, which only changes on a
//! release tag and so can't distinguish two builds of the same `0.x.y` cut.
//!
//! `build.rs` resolves the git fields at compile time and substitutes
//! `"unknown"` when the source tree isn't a git checkout (e.g. a crates.io
//! tarball, where `.git` is absent), so a published build still compiles and
//! reports a sensible value.
/// Crate version from `Cargo.toml` (`CARGO_PKG_VERSION`).
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
/// Short git commit SHA the binary was built from, with a `-dirty` suffix when
/// the working tree had uncommitted changes to tracked files at build time, or
/// `"unknown"` outside a git checkout.
pub const GIT_SHA: &str = env!("MOADIM_GIT_SHA");
/// Committer date (`YYYY-MM-DD`) of [`GIT_SHA`], or `"unknown"` outside a git checkout.
pub const BUILD_DATE: &str = env!("MOADIM_GIT_DATE");
/// Human-readable one-line version string, e.g. `0.1.0 (a1b2c3d 2026-06-19)`.
///
/// When the git provenance is unavailable (`GIT_SHA == "unknown"`), the suffix is
/// dropped and the bare crate version is returned.
pub fn long_version() -> String {
format_version(VERSION, GIT_SHA, BUILD_DATE)
}
/// Pure formatting core for [`long_version`], split out so both the
/// git-available and the `"unknown"` fallback branches are unit-testable
/// regardless of how the test binary itself was built.
fn format_version(version: &str, sha: &str, date: &str) -> String {
if sha == "unknown" {
version.to_string()
} else {
format!("{version} ({sha} {date})")
}
}
/// How often the running daemon checks whether the binary now sitting at its own `current_exe()`
/// path differs from the one this process was started from (#167). A daemon left running across an
/// in-place binary upgrade keeps executing the old compiled-in logic — including whatever it writes
/// into a routine's generated agent instructions with no signal to the operator that a restart
/// would pick up the newer build. Hourly is plenty: this is
/// an operator nudge, not a time-critical check, so it runs on its own cadence independent of
/// [`crate::routines::CLEANUP_INTERVAL`].
pub const VERSION_DRIFT_CHECK_INTERVAL: std::time::Duration =
std::time::Duration::from_secs(60 * 60);
/// Compare `running`, this process's own version string, against what `exe` reports for
/// `--version` right now, returning a warning message when they differ.
///
/// `exe` is queried by spawning it rather than reading Cargo/git metadata off disk, so this
/// reflects whatever binary is *actually* installed at that path, however it was built. Returns
/// `None` when `exe` cannot be run, exits non-zero, prints nothing, or reports the same version —
/// i.e. whenever there is nothing an operator needs to act on.
pub(crate) fn version_drift_warning(exe: &std::path::Path, running: &str) -> Option<String> {
let output = std::process::Command::new(exe)
.arg("--version")
.output()
.ok()?;
if !output.status.success() {
return None;
}
let on_disk = String::from_utf8_lossy(&output.stdout).trim().to_string();
if on_disk.is_empty() || on_disk == running {
return None;
}
Some(format!(
"moadim: running build ({running}) differs from the build now on disk at {} ({on_disk}) — \
restart the daemon (`moadim restart`) to pick up the update.",
exe.display()
))
}
/// Log [`version_drift_warning`]'s message for `exe`/`running`, if any.
///
/// Split out from the periodic-task closure that calls it so that closure has a single
/// unconditional call site — the branching (and the `log::warn!` call site itself) lives here,
/// where `exe`/`running` are injectable and so directly unit-testable.
pub(crate) fn warn_on_drift(exe: &std::path::Path, running: &str) {
if let Some(warning) = version_drift_warning(exe, running) {
log::warn!("{warning}");
}
}
#[cfg(test)]
#[path = "build_info_tests.rs"]
mod build_info_tests;