Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ semver = "1"
# Logging
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
tracing-appender = "0.2"

# Error handling
thiserror = "2"
Expand Down
19 changes: 18 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
//! Build script for saorsa-node.

use std::process::Command;

fn main() {
// Rerun if feature configuration changes
// Rerun if the git HEAD pointer changes (branch switch, detached HEAD, etc.)
println!("cargo:rerun-if-changed=.git/HEAD");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale commit hash on incremental builds

cargo:rerun-if-changed=.git/HEAD only tracks the HEAD pointer file (which typically contains ref: refs/heads/main — a static string that does not change when new commits are made on the current branch). Cargo will therefore skip re-running the build script after a git commit on the same branch, meaning the embedded SAORSA_GIT_COMMIT value becomes stale until something else triggers a full rebuild.

To keep the embedded hash up-to-date you need to also watch the ref that HEAD points to, as well as packed-refs (which is updated when refs are packed):

// Rerun if the git HEAD pointer changes (branch switch, detached HEAD, etc.)
println!("cargo:rerun-if-changed=.git/HEAD");
// Rerun if the current branch tip moves (new commits)
println!("cargo:rerun-if-changed=.git/refs/heads");
// Rerun if refs are packed (e.g. after `git gc`)
println!("cargo:rerun-if-changed=.git/packed-refs");
println!("cargo:rerun-if-changed=build.rs");

Alternatively, consider the vergen crate which handles all of these edge cases automatically.

Prompt To Fix With AI
This is a comment left during a code review.
Path: build.rs
Line: 7

Comment:
**Stale commit hash on incremental builds**

`cargo:rerun-if-changed=.git/HEAD` only tracks the HEAD *pointer* file (which typically contains `ref: refs/heads/main` — a static string that does not change when new commits are made on the current branch). Cargo will therefore skip re-running the build script after a `git commit` on the same branch, meaning the embedded `SAORSA_GIT_COMMIT` value becomes stale until something else triggers a full rebuild.

To keep the embedded hash up-to-date you need to also watch the ref that HEAD points to, as well as `packed-refs` (which is updated when refs are packed):

```rust
// Rerun if the git HEAD pointer changes (branch switch, detached HEAD, etc.)
println!("cargo:rerun-if-changed=.git/HEAD");
// Rerun if the current branch tip moves (new commits)
println!("cargo:rerun-if-changed=.git/refs/heads");
// Rerun if refs are packed (e.g. after `git gc`)
println!("cargo:rerun-if-changed=.git/packed-refs");
println!("cargo:rerun-if-changed=build.rs");
```

Alternatively, consider the [`vergen`](https://crates.io/crates/vergen) crate which handles all of these edge cases automatically.

How can I resolve this? If you propose a fix, please make it concise.

// Rerun if the current branch tip moves (new commits)
println!("cargo:rerun-if-changed=.git/refs/heads");
// Rerun if refs are packed (e.g. after `git gc`)
println!("cargo:rerun-if-changed=.git/packed-refs");
println!("cargo:rerun-if-changed=build.rs");

let commit = Command::new("git")
.args(["rev-parse", "--short", "HEAD"])
.output()
.ok()
.filter(|o| o.status.success())
.and_then(|o| String::from_utf8(o.stdout).ok())
.map_or_else(|| "unknown".to_string(), |s| s.trim().to_string());

println!("cargo:rustc-env=SAORSA_GIT_COMMIT={commit}");
}
25 changes: 25 additions & 0 deletions src/bin/saorsa-node/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ pub struct Cli {
#[arg(long, value_enum, default_value = "info", env = "RUST_LOG")]
pub log_level: CliLogLevel,

/// Log output format.
#[arg(long, value_enum, default_value = "text", env = "SAORSA_LOG_FORMAT")]
pub log_format: CliLogFormat,

/// Directory for log file output.
/// When set, logs are written to files in this directory instead of stdout.
/// Files rotate daily and are named saorsa-node.YYYY-MM-DD.log.
#[arg(long, env = "SAORSA_LOG_DIR")]
pub log_dir: Option<PathBuf>,

/// Maximum number of rotated log files to retain (only used with --log-dir).
/// Oldest files are deleted when this limit is reached. Rotation is daily.
#[arg(long, default_value = "7", env = "SAORSA_LOG_MAX_FILES")]
pub log_max_files: usize,

/// Network mode (production, testnet, or development).
/// Testnet mode uses relaxed IP diversity limits suitable for
/// single-provider deployments with many nodes per IP.
Expand Down Expand Up @@ -147,6 +162,16 @@ pub enum CliLogLevel {
Trace,
}

/// Log format CLI enum.
#[derive(Debug, Clone, Copy, ValueEnum, Default)]
pub enum CliLogFormat {
/// Plain text output (default).
#[default]
Text,
/// Structured JSON output.
Json,
}

/// Network mode CLI enum.
#[derive(Debug, Clone, Copy, ValueEnum, Default)]
pub enum CliNetworkMode {
Expand Down
62 changes: 58 additions & 4 deletions src/bin/saorsa-node/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
mod cli;

use clap::Parser;
use cli::Cli;
use cli::{Cli, CliLogFormat};
use saorsa_node::NodeBuilder;
use tracing::info;
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter, Layer};

#[tokio::main]
async fn main() -> color_eyre::Result<()> {
Expand All @@ -16,16 +17,69 @@ async fn main() -> color_eyre::Result<()> {
// Parse CLI arguments
let cli = Cli::parse();

// Extract logging options before consuming the CLI struct
let log_format = cli.log_format;
let log_dir = cli.log_dir.clone();
let log_max_files = cli.log_max_files;

// Initialize tracing
let log_level: String = cli.log_level.into();
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(&log_level));

// _guard must live for the duration of main() to ensure log flushing.
// The guard's Drop impl flushes buffered logs — it is intentionally held, not read.
#[allow(clippy::collection_is_never_read)]
let _guard: Option<tracing_appender::non_blocking::WorkerGuard>;

let layer: Box<dyn Layer<_> + Send + Sync> = match (log_format, log_dir) {
(CliLogFormat::Text, None) => {
_guard = None;
Box::new(fmt::layer())
}
(CliLogFormat::Json, None) => {
_guard = None;
Box::new(fmt::layer().json().flatten_event(true))
}
(CliLogFormat::Text, Some(dir)) => {
let file_appender = tracing_appender::rolling::Builder::new()
.rotation(tracing_appender::rolling::Rotation::DAILY)
.max_log_files(log_max_files)
.filename_prefix("saorsa-node")
.filename_suffix("log")
.build(dir)?;
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
_guard = Some(guard);
Box::new(fmt::layer().with_writer(non_blocking).with_ansi(false))
}
(CliLogFormat::Json, Some(dir)) => {
let file_appender = tracing_appender::rolling::Builder::new()
.rotation(tracing_appender::rolling::Rotation::DAILY)
.max_log_files(log_max_files)
.filename_prefix("saorsa-node")
.filename_suffix("log")
.build(dir)?;
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
_guard = Some(guard);
Box::new(
fmt::layer()
.json()
.flatten_event(true)
.with_writer(non_blocking)
.with_ansi(false),
)
}
};

tracing_subscriber::registry()
.with(fmt::layer())
.with(layer)
.with(filter)
.init();

info!("saorsa-node v{}", env!("CARGO_PKG_VERSION"));
info!(
version = env!("CARGO_PKG_VERSION"),
commit = env!("SAORSA_GIT_COMMIT"),
"saorsa-node starting"
);

// Build configuration
let config = cli.into_config()?;
Expand Down
16 changes: 7 additions & 9 deletions src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,18 +425,16 @@ impl RunningNode {
///
/// Returns an error if the node encounters a fatal error.
pub async fn run(&mut self) -> Result<()> {
info!("Starting saorsa-node");
info!("Node runtime loop starting");

// Start the P2P node
self.p2p_node
.start()
.await
.map_err(|e| Error::Startup(format!("Failed to start P2P node: {e}")))?;

info!(
"P2P node started, listening on {:?}",
self.p2p_node.listen_addrs().await
);
let addrs = self.p2p_node.listen_addrs().await;
info!(listen_addrs = ?addrs, "P2P node started");

// Emit started event
if let Err(e) = self.events_tx.send(NodeEvent::Started) {
Expand All @@ -463,9 +461,9 @@ impl RunningNode {
result = monitor.check_for_updates() => {
if let Ok(Some(upgrade_info)) = result {
info!(
"Upgrade available: {} -> {}",
upgrader.current_version(),
upgrade_info.version
current_version = %upgrader.current_version(),
new_version = %upgrade_info.version,
"Upgrade available"
);

// Send notification event
Expand All @@ -479,7 +477,7 @@ impl RunningNode {
info!("Starting auto-apply upgrade...");
match upgrader.apply_upgrade(&upgrade_info).await {
Ok(UpgradeResult::Success { version }) => {
info!("Upgrade to {} successful! Process will restart.", version);
info!(version = %version, "Upgrade successful, process will restart");
// If we reach here, exec() failed or not supported
}
Ok(UpgradeResult::RolledBack { reason }) => {
Expand Down
17 changes: 9 additions & 8 deletions src/upgrade/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,10 @@ impl UpgradeMonitor {

let delay = rollout.calculate_delay_for_version(&info.version);
info!(
"New version {} detected. Staged rollout delay: {}h {}m",
info.version,
delay.as_secs() / 3600,
(delay.as_secs() % 3600) / 60
new_version = %info.version,
delay_hours = delay.as_secs() / 3600,
delay_minutes = (delay.as_secs() % 3600) / 60,
"New version detected, staged rollout delay calculated"
);
}

Expand All @@ -269,8 +269,8 @@ impl UpgradeMonitor {

if elapsed >= delay {
info!(
"Staged rollout delay elapsed. Ready to upgrade to version {}",
info.version
version = %info.version,
"Staged rollout delay elapsed, ready to upgrade"
);
Ok(Some(info))
} else {
Expand Down Expand Up @@ -343,8 +343,9 @@ impl UpgradeMonitor {
let sig_asset = release.assets.iter().find(|a| a.name == sig_name)?;

info!(
"New version available: {} -> {}",
self.current_version, latest_version
current_version = %self.current_version,
new_version = %latest_version,
"New version available"
);

Some(UpgradeInfo {
Expand Down
Loading