nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 old,
3 new,
4 specifiedGems,
5 withData ? false,
6 # Use for `lib`.
7 pkgs ? import ../.. { },
8}:
9
10# Rename inputs to re-use those names.
11let
12 old' = old;
13 new' = new;
14 specifiedGems' = specifiedGems;
15in
16
17let
18 inherit (builtins)
19 attrNames
20 concatStrings
21 filter
22 genList
23 isNull
24 length
25 stringLength
26 toJSON
27 ;
28 inherit (pkgs.lib)
29 concatMapStringsSep
30 concatStringsSep
31 intersectLists
32 splitString
33 subtractLists
34 versionOlder
35 ;
36
37 # Keeps non-nulls in a list.
38 # Mirroring Ruby's `Array#compact`.
39 compact = filter (v: !(isNull v));
40
41 # The full gemsets attribute sets.
42 old = import old';
43 new = import new';
44
45 # All gem names.
46 allGems = attrNames (old // new);
47
48 # Gems found in both old and new.
49 keptGems = intersectLists (attrNames old) (attrNames new);
50
51 # Gems added or removed.
52 addedOrRemovedGems = subtractLists keptGems allGems;
53
54 # Gems specified in Gemfile.
55 specifiedGems = splitString "\n" specifiedGems';
56
57 # Gems that were not specified.
58 nonSpecifiedGems = subtractLists specifiedGems keptGems;
59
60 # Generates data for the summary tables
61 # This is also used for `failedChecks`.
62 versionChangeDataFor =
63 gems:
64 let
65 results = map (
66 name:
67 let
68 oldv = old.${name}.version or null;
69 newv = new.${name}.version or null;
70 in
71 if newv == oldv then
72 # Nothing changed. This will be filtered out.
73 null
74 else
75 {
76 inherit
77 name
78 ;
79 old = oldv;
80 new = newv;
81 }
82 ) gems;
83 in
84 compact results;
85
86 checkRegression =
87 entry: message:
88 let
89 isRemoval = isNull entry.new;
90 isAddition = isNull entry.old;
91 isRegression = versionOlder entry.new entry.old;
92 in
93 if
94 # Gems being added or gems being removed won't cause failures.
95 !isRemoval
96 && !isAddition
97 # A version being regressed is a failure.
98 && isRegression
99 then
100 message
101 else
102 null;
103
104 # This is a list of error messages to float up to the user.
105 # An empty list means no error.
106 failedChecks = compact (
107 [ ]
108 ++ (map (
109 entry:
110 checkRegression entry "Version regression for specified gem ${toJSON entry.name}, from ${toJSON entry.old} to ${toJSON entry.new}"
111 ) (versionChangeDataFor specifiedGems))
112 ++ (map (
113 entry:
114 checkRegression entry "Version regression for non-specified gem ${toJSON entry.name}, from ${toJSON entry.old} to ${toJSON entry.new}"
115 ) (versionChangeDataFor nonSpecifiedGems))
116 );
117
118 # Formats a version number (or null) as markdown.
119 gemVersionToMD = version: if isNull version then "*N/A*" else "`${version}`";
120
121 # Formats a `versionChangeDataFor` output as markdown.
122 versionChangeDataMD =
123 gems:
124 let
125 result = versionChangeDataFor gems;
126 in
127 map (
128 row:
129 [ row.name ]
130 ++ (map gemVersionToMD [
131 row.old
132 row.new
133 ])
134 ) result;
135
136 # Given a list of columns, and a list of list of column data,
137 # generates the markup for markdown table.
138 mkTable =
139 columns: entries:
140 let
141 entryToMarkdown = columns: "| ${concatStringsSep " | " columns} |";
142 sep = entryToMarkdown (map (_: "---") columns);
143 in
144 if length entries == 0 then
145 "> *No data...*"
146 else
147 ''
148 ${entryToMarkdown columns}
149 ${sep}
150 ${concatMapStringsSep "\n" entryToMarkdown entries}
151 '';
152
153 # The markdown report is built as this string.
154 report = ''
155 <!--
156 ----------------------------------------------
157 NOTE: You must copy this whole report section
158 to your pull request!
159 ----------------------------------------------
160 -->
161
162 #### Nixpkgs Ruby packages update report
163
164 **Specified gems changed:**
165
166 ${mkTable [ "Name" "old" "new" ] (versionChangeDataMD specifiedGems)}
167
168 **Gems added or removed:**
169
170 ${mkTable [ "Name" "old" "new" ] (versionChangeDataMD addedOrRemovedGems)}
171
172 <details>
173
174 <summary><strong>(Non-specified gem changes)</strong></summary>
175
176 ${mkTable [ "Name" "old" "new" ] (versionChangeDataMD nonSpecifiedGems)}
177
178 </details>
179
180 <!-- --------------- End ----------------- -->
181 '';
182in
183if (length failedChecks) > 0 then
184 # Fail the update script via `abort` on checks failure.
185 builtins.abort ''
186 ${"\n"}Gem upgrade aborted with the following failures:
187
188 ${concatMapStringsSep "\n" (msg: " - ${msg}") failedChecks}
189 ''
190else
191 # Output the report.
192 builtins.trace "(Report follows...)\n\n${report}" (
193 # And if `withData` is true, expose the data for REPL usage.
194 if withData then
195 {
196 inherit
197 # The gemsets used
198 old
199 new
200 # The lists of gems
201 allGems
202 specifiedGems
203 nonSpecifiedGems
204 addedOrRemovedGems
205 keptGems
206 ;
207 }
208 else
209 null
210 )