1
1
#!/usr/bin/env python3
2
2
3
3
import argparse
4
+ from collections import defaultdict
5
+ import graphlib
4
6
import json
5
7
import os
6
8
import subprocess
@@ -48,6 +50,9 @@ class GitHubActionPackage(TypedDict):
48
50
system : str
49
51
already_cached : bool
50
52
runs_on : RunsOnConfig
53
+ drvPath : str
54
+ neededSubstitutes : List [str ]
55
+ neededBuilds : List [str ]
51
56
postgresql_version : NotRequired [str ]
52
57
53
58
@@ -114,6 +119,9 @@ def parse_nix_eval_line(
114
119
"system" : data ["system" ],
115
120
"already_cached" : data .get ("cacheStatus" ) != "notBuilt" ,
116
121
"runs_on" : runs_on_config ,
122
+ "drvPath" : data ["drvPath" ],
123
+ "neededSubstitutes" : data .get ("neededSubstitutes" , []),
124
+ "neededBuilds" : data .get ("neededBuilds" , []),
117
125
}
118
126
except json .JSONDecodeError :
119
127
print (f"Skipping invalid JSON line: { line } " , file = sys .stderr )
@@ -133,8 +141,7 @@ def run_nix_eval_jobs(
133
141
134
142
for line in process .stdout :
135
143
package = parse_nix_eval_line (line , drv_paths , target )
136
- if package and not package ["already_cached" ]:
137
- print (f"Found package: { package ['attr' ]} " , file = sys .stderr )
144
+ if package :
138
145
yield package
139
146
140
147
if process .returncode and process .returncode != 0 :
@@ -149,6 +156,34 @@ def is_extension_pkg(pkg: GitHubActionPackage) -> bool:
149
156
return attrs [- 2 ] == "exts"
150
157
151
158
159
+ # thank you buildbot-nix https://github.com/nix-community/buildbot-nix/blob/985d069a2a45cf4a571a4346107671adc2bd2a16/buildbot_nix/buildbot_nix/build_trigger.py#L297
160
+ def sort_pkgs_by_closures (jobs : list [GitHubActionPackage ]) -> list [GitHubActionPackage ]:
161
+ sorted_jobs = []
162
+
163
+ # Prepare job dependencies
164
+ job_set = {job ["drvPath" ] for job in jobs }
165
+ job_closures = {
166
+ k ["drvPath" ]: set (k ["neededSubstitutes" ])
167
+ .union (set (k ["neededBuilds" ]))
168
+ .intersection (job_set )
169
+ .difference ({k ["drvPath" ]})
170
+ for k in jobs
171
+ }
172
+
173
+ sorter = graphlib .TopologicalSorter (job_closures )
174
+
175
+ for item in sorter .static_order ():
176
+ i = 0
177
+ while i < len (jobs ):
178
+ if item == jobs [i ]["drvPath" ]:
179
+ sorted_jobs .append (jobs [i ])
180
+ del jobs [i ]
181
+ else :
182
+ i += 1
183
+
184
+ return sorted_jobs
185
+
186
+
152
187
def main () -> None :
153
188
parser = argparse .ArgumentParser (
154
189
description = "Generate GitHub Actions matrix for Nix builds"
@@ -168,23 +203,22 @@ def main() -> None:
168
203
169
204
cmd = build_nix_eval_command (max_workers , flake_output )
170
205
171
- gh_action_packages = list (run_nix_eval_jobs (cmd , flake_output ))
206
+ gh_action_packages = sort_pkgs_by_closures (
207
+ list (run_nix_eval_jobs (cmd , flake_output ))
208
+ )
172
209
173
210
if args .target == "extensions" :
174
211
# filter to only include extension packages and add postgresql_version field
175
212
gh_action_packages = [
176
213
{** pkg , "postgresql_version" : pkg ["attr" ].split ("." )[- 3 ]}
177
214
for pkg in gh_action_packages
178
- if is_extension_pkg (pkg )
215
+ if is_extension_pkg (pkg ) and not pkg [ "already_cached" ]
179
216
]
180
217
181
218
# Group packages by system
182
- grouped_by_system = {}
219
+ grouped_by_system = defaultdict ( list )
183
220
for pkg in gh_action_packages :
184
- system = pkg ["system" ]
185
- if system not in grouped_by_system :
186
- grouped_by_system [system ] = []
187
- grouped_by_system [system ].append (pkg )
221
+ grouped_by_system [pkg ["system" ]].append (pkg )
188
222
189
223
# Create output with system-specific matrices
190
224
gh_output = {}
0 commit comments