forge/cmd/
update.rs

1use clap::{Parser, ValueHint};
2use eyre::{Context, Result};
3use foundry_cli::{
4    opts::Dependency,
5    utils::{Git, LoadConfig},
6};
7use foundry_config::{impl_figment_convert_basic, Config};
8use std::path::PathBuf;
9
10/// CLI arguments for `forge update`.
11#[derive(Clone, Debug, Parser)]
12pub struct UpdateArgs {
13    /// The dependencies you want to update.
14    dependencies: Vec<Dependency>,
15
16    /// The project's root path.
17    ///
18    /// By default root of the Git repository, if in one,
19    /// or the current working directory.
20    #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")]
21    root: Option<PathBuf>,
22
23    /// Override the up-to-date check.
24    #[arg(short, long)]
25    force: bool,
26
27    /// Recursively update submodules.
28    #[arg(short, long)]
29    recursive: bool,
30}
31impl_figment_convert_basic!(UpdateArgs);
32
33impl UpdateArgs {
34    pub fn run(self) -> Result<()> {
35        let config = self.load_config()?;
36        let (root, paths) = dependencies_paths(&self.dependencies, &config)?;
37        // fetch the latest changes for each submodule (recursively if flag is set)
38        let git = Git::new(&root);
39        if self.recursive {
40            // update submodules recursively
41            git.submodule_update(self.force, true, false, true, paths)
42        } else {
43            // update root submodules
44            git.submodule_update(self.force, true, false, false, paths)?;
45            // initialize submodules of each submodule recursively (otherwise direct submodule
46            // dependencies will revert to last commit)
47            git.submodule_foreach(false, "git submodule update --init --progress --recursive")
48        }
49    }
50}
51
52/// Returns `(root, paths)` where `root` is the root of the Git repository and `paths` are the
53/// relative paths of the dependencies.
54pub fn dependencies_paths(deps: &[Dependency], config: &Config) -> Result<(PathBuf, Vec<PathBuf>)> {
55    let git_root = Git::root_of(&config.root)?;
56    let libs = config.install_lib_dir();
57
58    let mut paths = Vec::with_capacity(deps.len());
59    for dep in deps {
60        let name = dep.name();
61        let dep_path = libs.join(name);
62        let rel_path = dep_path
63            .strip_prefix(&git_root)
64            .wrap_err("Library directory is not relative to the repository root")?;
65        if !dep_path.exists() {
66            eyre::bail!("Could not find dependency {name:?} in {}", dep_path.display());
67        }
68        paths.push(rel_path.to_owned());
69    }
70    Ok((git_root, paths))
71}