Skip to main content

lux_cli/
update.rs

1use clap::Args;
2use eyre::{eyre, Context, Result};
3use itertools::Itertools;
4use lux_lib::package::{PackageName, PackageReq};
5use lux_lib::progress::{MultiProgress, ProgressBar};
6use lux_lib::remote_package_db::RemotePackageDB;
7use lux_lib::rockspec::lua_dependency::LuaDependencyType;
8use lux_lib::workspace::Workspace;
9use lux_lib::{config::Config, operations};
10
11#[derive(Args)]
12pub struct Update {
13    /// Skip the integrity checks for installed rocks when syncing the project lockfile.
14    #[arg(long)]
15    no_integrity_check: bool,
16
17    /// Upgrade packages in the project's lux.toml (if operating on a project)
18    #[arg(long)]
19    toml: bool,
20
21    /// Packages to update.
22    /// When used with the --toml flag in a project, these must be package names.
23    packages: Option<Vec<PackageReq>>,
24
25    /// Build dependencies to update.
26    /// Also called `dev`.
27    /// When used with the --toml flag in a project, these must be package names.
28    #[arg(short, long, alias = "dev", visible_short_aliases = ['d', 'b'])]
29    build: Option<Vec<PackageReq>>,
30
31    /// Build dependencies to update.
32    /// When used with the --toml flag in a project, these must be package names.
33    #[arg(short, long)]
34    test: Option<Vec<PackageReq>>,
35
36    /// Project to modify.
37    #[arg(short, long, visible_short_alias = 'p')]
38    package: Option<PackageName>,
39}
40
41pub async fn update(args: Update, config: Config) -> Result<()> {
42    let progress = MultiProgress::new_arc(&config);
43    progress.map(|p| p.add(ProgressBar::from("🔎 Looking for updates...".to_string())));
44
45    if args.toml {
46        let mut workspace = Workspace::current_or_err()?;
47
48        let progress = MultiProgress::new(&config);
49        let bar = progress.map(|progress| progress.new_bar());
50        let db = RemotePackageDB::from_config(&config, &bar).await?;
51        let package_names = to_package_names(args.packages.as_ref())?;
52        let mut upgrade_all = true;
53        if let Some(packages) = package_names {
54            upgrade_all = false;
55            if let Some(package) = &args.package {
56                let project = workspace.select_member_mut(package)?;
57                project
58                    .upgrade(LuaDependencyType::Regular(packages.iter().collect()), &db)
59                    .await?;
60            } else {
61                for project in workspace.members_mut().iter_mut() {
62                    project
63                        .upgrade(LuaDependencyType::Regular(packages.iter().collect()), &db)
64                        .await?;
65                }
66            }
67        }
68        let build_package_names = to_package_names(args.build.as_ref())?;
69        if let Some(packages) = build_package_names {
70            upgrade_all = false;
71            if let Some(package) = &args.package {
72                let project = workspace.select_member_mut(package)?;
73                project
74                    .upgrade(LuaDependencyType::Build(packages.iter().collect()), &db)
75                    .await?;
76            } else {
77                for project in workspace.members_mut().iter_mut() {
78                    project
79                        .upgrade(LuaDependencyType::Build(packages.iter().collect()), &db)
80                        .await?;
81                }
82            }
83        }
84        let test_package_names = to_package_names(args.test.as_ref())?;
85        if let Some(packages) = test_package_names {
86            upgrade_all = false;
87            if let Some(package) = &args.package {
88                let project = workspace.select_member_mut(package)?;
89                project
90                    .upgrade(LuaDependencyType::Test(packages.iter().collect()), &db)
91                    .await?;
92            } else {
93                for project in workspace.members_mut().iter_mut() {
94                    project
95                        .upgrade(LuaDependencyType::Test(packages.iter().collect()), &db)
96                        .await?;
97                }
98            }
99        }
100        if upgrade_all {
101            if let Some(package) = &args.package {
102                let project = workspace.select_member_mut(package)?;
103                project.upgrade_all(&db).await?;
104            } else {
105                for project in workspace.members_mut().iter_mut() {
106                    project.upgrade_all(&db).await?;
107                }
108            }
109        }
110    }
111
112    let updated_packages = operations::Update::new(&config)
113        .progress(progress)
114        .packages(args.packages)
115        .build_dependencies(args.build)
116        .test_dependencies(args.test)
117        .validate_integrity(!args.no_integrity_check)
118        .update()
119        .await
120        .wrap_err("update failed.")?;
121
122    if updated_packages.is_empty() {
123        println!("Nothing to update.");
124        return Ok(());
125    }
126
127    Ok(())
128}
129
130fn to_package_names(packages: Option<&Vec<PackageReq>>) -> Result<Option<Vec<PackageName>>> {
131    if packages.is_some_and(|pkgs| !pkgs.iter().any(|pkg| pkg.version_req().is_any())) {
132        return Err(eyre!(
133            "Cannot use version constraints to upgrade dependencies in lux.toml."
134        ));
135    }
136    Ok(packages
137        .as_ref()
138        .map(|pkgs| pkgs.iter().map(|pkg| pkg.name()).cloned().collect_vec()))
139}