File: deps.nu

package info (click to toggle)
rust-coreutils 0.0.30-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 17,388 kB
  • sloc: sh: 1,088; python: 407; javascript: 72; makefile: 51
file content (130 lines) | stat: -rw-r--r-- 4,588 bytes parent folder | download | duplicates (2)
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# This is a script to analyze the dependencies of this project.
# It is a replacement of / complement to
#
#  - cargo tree (used by this script)
#  - cargo deps
#  - cargo deny
#
# The idea is that by calling all_dep_info, you get a table of all dependencies
# in Cargo.lock, with a few additional columns based on some other tools.
# Currently, these tools are
#
#  - cargo tree
#  - the crates.io API
#
# The most useful columns in the table are:
#
#  - `name`: the name of the crate.
#  - `version`: the version of the crate.
#  - `num_versions`: the number of versions in Cargo.lock.
#  - `normal_dep`: whether the crate is a normal dependency.
#  - `build_dep`: whether the crate is a build dependency.
#  - `dev_dep`: whether the crate is a dev dependency.
#  - `organization`: the GitHub/GitLab organization or user of the repository of the crate.
#  - `repository_name`: the name of the repository the crate is in. The format is "{owner}/{repo}".
#  - `dependencies`: direct dependencies of the crate (in the format of Cargo.lock).
#
# To use this script, start Nushell (tested only on version 0.82.0), import the library and
# call `all_dep_info`:
#
# ```
# > nu
# > use util/deps.nu
# > let dep = (deps all_dep_info)
# ```
#
# Then you can perform analysis. For example, to group the dependencies by organization:
#
# ```
# > $dep | group-by organization
# ```
#
# Or to find all crates with multiple versions (like cargo deny):
# ```
# > $dep | where num_versions > 1
# ```
#
# Ideas to expand this:
#
#  - Figure out the whole dependency graph
#  - Figure out which platforms and which features enable which crates
#  - Figure out which utils require which crates
#  - Count the number of crates on different platforms
#  - Add license information
#  - Add functions to perform common analyses
#  - Add info from cargo bloat
#  - Add MSRV info
#  - Add up-to-date info (the necessary info is there, it just needs to be derived)
#  - Check the number of owners/contributors
#  - Make a webpage to more easily explore the data

# Read the names output by cargo tree
export def read_tree_names [edge_kind: string, features: list<string>]: any -> table<> {
    cargo tree --edges $edge_kind --features ($features | str join ",")
        | parse -r "(?P<name>[a-zA-Z0-9_-]+) v(?P<version>[0-9.]+)"
}

# Read the crates.io info for a list of crates names
def read_crates_io [names: list<string>] -> any -> table<> {
    let total = $names | length
    $names | enumerate | par-each {|name|
        print $"($name.index)/($total): ($name.item)"
        http get $"https://crates.io/api/v1/crates/($name.item)" | get crate
    }
}

# Add column for a dependency type
def add_dep_type [dep_type: string, features: list<string>]: table<> -> table<> {
    let input_table = $in
    let table = read_tree_names $dep_type $features
    $input_table | insert $"($dep_type)_dep" {|outer|
        $table | any {|inner|
            $inner.name == $outer.name and $inner.version == $outer.version
        }
    }
}

export def all_dep_info [] {
    let features = [unix, feat_selinux]

    let lock = open Cargo.lock | from toml | get package
    
    $lock
    # Add number of versions
    | join ($lock | group-by name | transpose | update column1 { length } | rename name num_versions) name
    # Add dependency types
    | add_dep_type normal $features
    | add_dep_type build $features
    | add_dep_type dev $features
    | insert used {|x| $x.normal_dep or $x.build_dep or $x.dev_dep}
    # Add crates.io info
    | join (read_crates_io ($lock.name | uniq)) name
    # Add GH org or user info
    # The organization is an indicator that crates should be treated as one dependency.
    # However, there are also unrelated projects by a single organization, so it's not
    # clear.
    | insert organization {|x|
        let repository = $x.repository?
        if ($repository == null) { "" } else {
            $repository | url parse | get path | path split | get 1
        }
    }
    # Add repository (truncate everything after repo name)
    # If we get a url like
    #  https://github.com/uutils/coreutils/tree/src/uu/ls
    # we only keep
    #  uutils/coreutils
    # The idea is that crates in the same repo definitely belong to the same project and should
    # be treated as one dependency.
    | insert repository_name {|x|
        let repository = $x.repository?
        if ($repository == null) { '' } else {
            $repository
            | url parse
            | get path
            | path split
            | select 1 2
            | path join
        }
    }
}