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
93
94
95
96
97
98
99
100
101
102
103
use std::collections::HashMap;

use anyhow::{anyhow, Result};

use crate::configuration_file::ConfigurationFile;
use crate::monorepo_manifest::MonorepoManifest;
use crate::package_manifest::DependencyGroup;

#[derive(Clone)]
struct UnpinnedDependency {
    name: String,
    actual: String,
    expected: String,
}

pub fn pin_version_numbers_in_internal_packages(opts: crate::opts::Pin) -> Result<()> {
    let lerna_manifest = MonorepoManifest::from_directory(&opts.root)?;
    let mut package_manifest_by_package_name =
        lerna_manifest.package_manifests_by_package_name()?;

    let package_version_by_package_name: HashMap<String, String> = package_manifest_by_package_name
        .values()
        .map(|package| {
            (
                package.contents.name.to_owned(),
                package.contents.version.to_owned(),
            )
        })
        .collect();

    let mut exit_code = 0;

    for package_manifest in package_manifest_by_package_name.values_mut() {
        let mut dependencies_to_update: Vec<UnpinnedDependency> = Vec::new();
        for dependency_group in DependencyGroup::VALUES.iter() {
            if let Some(mut unpinned_dependencies) = package_manifest
                .get_dependency_group_mut(dependency_group)
                .map(|dependencies| -> Vec<UnpinnedDependency> {
                    dependencies
                        .into_iter()
                        .filter_map(
                            |(dependency_name, dependency_version)| -> Option<UnpinnedDependency> {
                                package_version_by_package_name
                                    .get(dependency_name)
                                    .and_then(|internal_dependency_declared_version| {
                                        let used_dependency_version = dependency_version
                                            .as_str()
                                            .expect(
                                                "Expected each dependency version to be a string",
                                            )
                                            .to_owned();
                                        match used_dependency_version
                                            .eq(internal_dependency_declared_version)
                                        {
                                            true => None,
                                            false => {
                                                *dependency_version = serde_json::Value::String(
                                                    internal_dependency_declared_version.to_owned(),
                                                );
                                                Some(UnpinnedDependency {
                                                    name: dependency_name.to_owned(),
                                                    actual: used_dependency_version,
                                                    expected: internal_dependency_declared_version
                                                        .to_owned(),
                                                })
                                            }
                                        }
                                    })
                            },
                        )
                        .collect()
                })
            {
                dependencies_to_update.append(&mut unpinned_dependencies)
            }
        }

        if !dependencies_to_update.is_empty() {
            if opts.check_only {
                exit_code = 1;
                println!(
                    "File contains unexpected dependency versions: {:?}",
                    package_manifest.path()
                );
                for dependency in dependencies_to_update {
                    println!(
                        "\tdependency: {:?}\texpected: {:?}\tgot: {:?}",
                        dependency.name, dependency.expected, dependency.actual
                    );
                }
            } else {
                package_manifest.write()?;
            }
        }
    }

    if opts.check_only && exit_code != 0 {
        return Err(anyhow!(
            "Found unexpected dependency versions for internal packages"
        ));
    }
    Ok(())
}