summaryrefslogtreecommitdiffstats
path: root/nix-script-diff-generations.sh
blob: 5942b66751aee903a26293e80810bae684612c0d (plain)
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
#!/usr/bin/env bash

source $(dirname ${BASH_SOURCE[0]})/nix-utils.sh

usage() {
    cat <<EOS
    $(help_synopsis "${BASH_SOURCE[0]}" "[-s | -u] [-n <a..b>] [-h]")

        -s       Use system generation for diffing
        -u       Use system generation for diffing
        -n a..b  Diff these generations
        -h       Show this help and exit

    Show the diff between two generations, so the installed and removed
    packages.

    Example usage:

        # To show the diff of the system generation 123 to 145
        # with verbosity on
        nix-script -v diff-generations -s -n 123..145

$(help_end "${BASH_SOURCE[0]}")
EOS
}

__SYSTEM=0
__USER=0

GEN_A=
GEN_B=

while getopts "sun:h" OPTION
do
    case $OPTION in

        s)
            stdout "Setting profile: system"
            __SYSTEM=1
            __USER=0
            ;;

        u)
            stdout "Setting profile: user ($USER)"
            __SYSTEM=0
            __USER=1
            ;;

        n)
            GEN_A=$(echo $OPTARG | cut -d "." -f 1)
            GEN_B=$(echo $OPTARG | cut -d "." -f 3)

            dbg "Parsing generations: $OPTARG"
            dbg "Parsing generations: GEN_A: $GEN_A"
            dbg "Parsing generations: GEN_B: $GEN_B"

            [[ -z "$GEN_A" || -z "$GEN_B" ]] && \
                stderr "Parsing error for '$OPTARG'" && usage && exit 1
            ;;

        h)
            usage
            exit 1
            ;;
    esac
done

#
# Helper to generate a path for the profile we want to diff with
#
gen_path() {
    [[ $__SYSTEM -eq 1 ]] && echo "/nix/var/nix/profiles/system-${1}-link"
    [[ $__USER -eq 1 ]] && echo "/nix/var/nix/profiles/per-user/$USER/profile-${1}-link"
}

DIR_A=$(gen_path $GEN_A)
DIR_B=$(gen_path $GEN_B)

dbg "VERBOSE         : $VERBOSE"
dbg "SYSTEM          : $__SYSTEM"
dbg "USER            : $__USER"
dbg "Generation A    : $GEN_A"
dbg "from directory  : $DIR_A"
dbg "Generation B    : $GEN_B"
dbg "from directory  : $DIR_B"

#
# Error checking whether the generations exist.
#
[[ -z "$GEN_A" || -z "$GEN_B" ]] && stderr "No generations"     && exit 1
[[ ! -e $DIR_A ]] && stderr "Generation $GEN_A does not exist." && exit 1
[[ ! -e $DIR_B ]] && stderr "Generation $GEN_B does not exist." && exit 1

#
# Querying the store for the stuff in a generation A
#
versA=$(mktemp)
stdout "TMP file '$versA' created"
nix-store -qR $DIR_A | sort -t'-' -k 2 > $versA
stdout "Generation packages written for $GEN_A"

#
# Querying the store for the stuff in a generation B
#
versB=$(mktemp)
stdout "TMP file '$versB' created"
nix-store -qR $DIR_B | sort -t'-' -k 2 > $versB
stdout "Generation packages written for $GEN_B"

stdout "Diffing now..."

diff -u $versA $versB | \
    # Select only lines that differ.
    # (no context, no file name, no line number, etc.)
    grep '^[+-]/nix/store' | \
    # Remove the "/nix/store/<hash>" garbage.
    # Add a space instead, to separate the [+-] from the name.
    sed 's:/nix/store/[^-]*-: :' | \
    # sort by name, then prefer '+' over '-'.
    sort -k 2 -k 1,1r

stdout "Removing tmp directories"

rm $versA
rm $versB

stdout "Ready"