File: dep-diff

package info (click to toggle)
puppetdb 8.8.1-1~exp1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 19,692 kB
  • sloc: javascript: 23,285; ruby: 5,620; sh: 3,457; python: 389; xml: 114; makefile: 38
file content (202 lines) | stat: -rwxr-xr-x 7,135 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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#!/usr/bin/env bash
":"; # -*-clojure-*-
":"; set -ueo pipefail
":"; script_home="$(cd "$(dirname "$0")" && pwd)"
":"; exec "$script_home/clj-1.11" "$0" "$@"

(require
 '[clojure.edn :as edn]
 '[clojure.java.io :as io]
 '[clojure.set :as set]
 '[clojure.string :as str])
(import
 '(clojure.lang ExceptionInfo)
 '(java.io PushbackReader))

(defn prs [stream & items]
  (binding [*out* stream]
    (apply pr items)))

(defn prns [stream & items]
  (binding [*out* stream]
    (apply prn items)))

(defn msg [stream & items]
  (binding [*out* stream]
    (apply print items)))

(defn msgn [stream & items]
  (binding [*out* stream]
    (apply println items)))

(defn usage [stream]
  (msgn stream (str/triml "
Usage:
    dep-diff --help
    dep-diff [OPT...] [--] OLD_TREE_PATH NEW_TREE_PATH
  OPT:
     --mismatches | --ignore-exclusions | --ignore-id X/Y

  Examines two dependency lists as-per \"lein deps :tree\" and reports
  any differences with respect to the dependency names and versions.
  If --mismatches is specified, then only reports mismatched versions
  of dependencies the two lists have in common.  If
  --ignore-exclusions is specified, then does not consider exclusions
  when comparing the dependencies.  Specific dependencies can be
  excluded from consideration via --ignore-id, e.g. --ignore-id
  org.clojure/tools.cli.")))

(defn exit [rc]
  (throw (ex-info "" {:kind ::exit ::rc 1})))

(defn tree->dep-seq [stream]
  (with-open [r (io/reader stream)
              pbr (PushbackReader. r)]
    (doall
     (let [eof (Object.)]
       (take-while #(not= % eof)
                   (repeatedly #(edn/read {:eof eof} pbr)))))))

(defn validate-dep-seq [ds]
  (doseq [dep ds]
    (when-not (> (count ds) 1)
      (msgn *err* "dependency seq len < 2:" (pr-str dep))
      (exit 2))
    (when-not (symbol? (first dep))
      (msgn *err* "dependency name is not a symbol:" (pr-str dep))
      (exit 2))
    (when-not (string? (second dep))
      (msgn *err* "dependency version is not a string: " (pr-str dep))
      (exit 2)))
  (doseq [[name deps-for-name] (group-by first ds)
          :when (not= 1 (count deps-for-name))]
    (let [versions (map second deps-for-name)]
      (when (not= 1 (count (distinct versions)))
        (msgn *err* "unexpected duplicates:" (pr-str deps-for-name))
        (exit 2))))
  ds)

(defn strip-bc-scope [dep]
  (if (#{'org.bouncycastle/bcutil-jdk18on 'org.bouncycastle/bcpkix-jdk18on 'org.bouncycastle/bcprov-jdk18on}
       (first dep))
    (letfn [(strip [remainder]
              (when (seq remainder)
                (if (= '(:scope "provided") (take 2 remainder))
                  (strip (drop 2 remainder))
                  (cons (first remainder)
                        (strip (rest remainder))))))]
      (vec (strip dep)))
    dep))

(defn strip-exclusions [dep]
  (letfn [(strip [remainder]
            (when (seq remainder)
              (if (= :exclusions (first remainder))
                (strip (drop 2 remainder))
                (cons (first remainder)
                      (strip (rest remainder))))))]
    (vec (strip dep))))

(defn not-puppetdb? [dep]
  (not= 'puppetlabs/puppetdb (first dep)))

(defn display-diff [{:keys [ignore-exclusions? mismatches-only? ignore-puppetdb?
                            ignore-ids pe-bouncy-castle-provided? out]
                     :or {out *out*} :as opts}
                    deps-1 deps-2]
  (doseq [name (-> (concat (keys deps-1) (keys deps-2)) distinct sort)
          :when (or (empty? ignore-ids) (not (ignore-ids name)))
          :let [old (get deps-1 name)
                new (get deps-2 name)]]
    (if (and (seq old) (seq new))
      (let [relevant-set (fn [s]
                           ((cond-> set
                              ignore-exclusions? (comp (partial map strip-exclusions))
                              ignore-puppetdb? (comp (partial filter not-puppetdb?))
                              pe-bouncy-castle-provided? (comp (partial map strip-bc-scope)))
                            s))
            relevant-old (relevant-set old)
            relevant-new (relevant-set new)]
        (when (not= relevant-old relevant-new)
          (doseq [dep (sort-by second (set/difference relevant-old relevant-new))]
            (prns out '- dep))
          (doseq [dep (sort-by second (set/difference relevant-new relevant-old))]
            (prns out '+ dep))))
      ;; We only have old or new
      (if-not mismatches-only?
        (if (seq old)
          (doseq [dep (sort-by second old)]
            (prns out '- dep))
          (doseq [dep (sort-by second new)]
            (prns out '+ dep)))))))

(defn dispatch-diff [opts old new]
  (apply display-diff opts
         (map (fn [path]
                (with-open [r (io/input-stream path)]
                  (group-by first (-> r tree->dep-seq validate-dep-seq))))
              [old new]))
  0)

(defn args->opts [args]
  (loop [args args
         opts {:positional []
               :ignore-ids #{}}]
    (if-not (seq args)
      opts
      (case (first args)
        "--help" (recur (rest args) (assoc opts :help? true))
        "--mismatches" (recur (rest args) (assoc opts :mismatches-only? true))
        "--ignore-exclusions" (recur (rest args)
                                     (assoc opts :ignore-exclusions? true))
        "--ignore-puppetdb" (recur (rest args)
                                     (assoc opts :ignore-puppetdb? true))
        "--ignore-id" (recur (nthrest args 2)
                             (update opts :ignore-ids #(conj % (-> args (nth 1) symbol))))
        "--allow-pe-bc-provided" (recur (rest args)
                                        (assoc opts :pe-bouncy-castle-provided? true))
        (cond
          (= "--" (first args)) (update opts :positional conj (rest args))

          (str/starts-with? (first args) "-")
          (do
            (msgn *err* "dep-diff: unknown argument" (pr-str (first args)))
            (exit 2))

          :else (recur (rest args) (update opts :positional conj (first args))))))))

(defn validate-opts [opts]
  (if (:help? opts)
    (when (seq (dissoc :help? opts))
      (usage *err*)
      (exit 2))
    (when (not= 2 (count (:positional opts)))
      (msgn *err* "dep-diff: please specify two comparison paths")
      (usage *err*)
      (exit 2)))
  opts)

(defn run-cmd [args]
  (let [opts (-> args args->opts validate-opts)]
    (if (:help? opts)
      (do (usage *out*) 0)
      (apply dispatch-diff
             (merge {:out *out*} (select-keys opts [:mismatches-only? :ignore-exclusions?
                                                    :ignore-puppetdb? :ignore-ids
                                                    :pe-bouncy-castle-provided?]))
             (:positional opts)))))

(defn main [args]
  (let [rc (try
             (run-cmd args)
             (catch ExceptionInfo ex
               (let [data (ex-data ex)]
                 (when-not (= ::exit (:kind data))
                   (throw ex))
                 (::rc data))))]
    (shutdown-agents)
    (flush)
    (binding [*out* *err*] (flush))
    (System/exit rc)))

(main *command-line-args*)