Skip to main content

mars_agents/cli/
upgrade.rs

1//! `mars upgrade` — upgrade dependencies to newest versions.
2
3use crate::error::MarsError;
4use crate::sync::{DependencyUpsertChange, ResolutionMode, SyncOptions, SyncRequest};
5use crate::types::SourceName;
6
7use super::output;
8
9/// Arguments for `mars upgrade`.
10#[derive(Debug, clap::Args)]
11pub struct UpgradeArgs {
12    /// Specific dependencies to upgrade (default: all).
13    pub names: Vec<String>,
14
15    /// Bump direct dependency version constraints to resolved latest tags.
16    #[arg(long)]
17    pub bump: bool,
18
19    /// Show per-item detail for launch-time fields handled by meridian at spawn.
20    #[arg(long)]
21    pub verbose: bool,
22
23    /// Ignore package `requires-mars` version constraints.
24    #[arg(long)]
25    pub ignore_requires_mars: bool,
26
27    /// Ignore package `requires-meridian` version constraints.
28    #[arg(long)]
29    pub ignore_requires_meridian: bool,
30}
31
32/// Run `mars upgrade`.
33pub fn run(args: &UpgradeArgs, ctx: &super::MarsContext, json: bool) -> Result<i32, MarsError> {
34    let request = SyncRequest {
35        resolution: ResolutionMode::Maximize {
36            targets: args
37                .names
38                .iter()
39                .map(|s| SourceName::from(s.as_str()))
40                .collect(),
41            bump: args.bump,
42        },
43        mutation: None,
44        options: SyncOptions {
45            ignore_requires_mars: args.ignore_requires_mars,
46            ignore_requires_meridian: args.ignore_requires_meridian,
47            ..SyncOptions::default()
48        },
49        recovery: crate::sync::RecoveryPolicy::DeferOnUnreadable,
50        lossiness_mode: if args.verbose {
51            crate::diagnostic::LossinessMode::Verbose
52        } else {
53            crate::diagnostic::LossinessMode::Surface
54        },
55    };
56
57    let report = crate::sync::execute(ctx, &request)?;
58
59    if args.bump && !json {
60        print_bump_messages(&report.dependency_changes);
61    }
62    output::print_sync_report(&report, json, true);
63
64    Ok(if report.recovery_halt.is_some() { 2 } else { 0 })
65}
66
67fn print_bump_messages(changes: &[DependencyUpsertChange]) {
68    for change in changes {
69        if change.old_version == change.new_version {
70            continue;
71        }
72        let from = change.old_version.as_deref().unwrap_or("latest");
73        let to = change.new_version.as_deref().unwrap_or("latest");
74        output::print_info(&format!(
75            "bumped dependency `{}`: {from} -> {to}",
76            change.name
77        ));
78    }
79}