libguestfs: Use python3 for builds and the Python PIP package.
[goaljobs-goals.git] / fedora.ml
1 (* Various useful functions for handling Fedora packages & rebuilds. *)
2
3 open ExtString
4 open Printf
5
6 open Goaljobs
7
8 open Config
9
10 (* Get the current version of a package. *)
11 let fedora_verrel pkg branch =
12   shout "
13     cd %s
14     fedpkg verrel
15   " (fedora_repo pkg branch)
16
17 (* Note most of these assume that the package is already cloned
18  * under ~/d/fedora using:
19  *
20  * cd ~/d/fedora
21  * fedpkg clone -B pkgname
22  *)
23
24 (* Take a list of Fedora source packages and work out the dependencies
25  * (only within the list of source packages).  Returns a list of
26  * (package, [list of deps...])
27  *)
28 let dependencies branch source_packages =
29   (* For each source package, get the list of binary packages that it
30    * provides.  XXX Not sure if this is totally technically correct, but
31    * it seems to work.
32    *)
33   let bin_to_src =
34     List.concat (
35       List.map (
36         fun pkg ->
37           let provides =
38             shlines "rpmspec -q --provides %s | awk '{print $1}'"
39               (fedora_specfile pkg branch) in
40           List.map (fun bin -> (bin, pkg)) provides
41       ) source_packages
42     ) in
43
44   (* For each package, get the list of build requires that appear
45    * elsewhere in the list of packages.
46    *)
47   let mem dep = List.mem dep source_packages in
48
49   List.map (
50     fun pkg ->
51       let deps =
52         shlines "rpmspec -q --buildrequires %s | awk '{print $1}'"
53           (fedora_specfile pkg branch) in
54       let deps = List.map (
55         fun dep ->
56           try List.assoc dep bin_to_src
57           with Not_found -> "xxx" (* filtered out in next line *)
58       ) deps in
59       let deps = Utils.sort_uniq (List.filter mem deps) in
60       (* eprintf "%s <- %s\n" pkg (String.concat " " deps); *)
61       pkg, deps
62   ) source_packages
63
64 let contains_substring substr str =
65   try ignore (String.find str substr); true
66   with Invalid_string -> false
67
68 (* Not helped by the fact that the 'koji' tool actively
69  * resists automation: RHBZ#760924.
70  *)
71
72 (* XXX koji_build_state verrel: If you do a build and it fails, then
73  * do another build without bumping the release field, 'koji buildinfo'
74  * seems to always return the failed build, at least until the second
75  * build completes.  This means the code below fails.  Unclear how it
76  * can be fixed, but best to always bump the release to avoid the
77  * problem.
78  *)
79
80 (* Get build state. *)
81 let rec koji_build_state verrel =
82   fst (koji_build_state_task verrel)
83
84 (* Get build state and task ID. *)
85 and koji_build_state_task =
86   let state = Pcre.regexp "State: (\\w+)" in
87   let task_id = Pcre.regexp "Task: (\\d+)" in
88   let no_such_build = contains_substring "No such build" in
89   fun verrel ->
90     (* For speed, if a build is complete memoize it. *)
91     let key = sprintf "koji_build_complete_%s" verrel in
92     if memory_exists key then
93       `Complete, None
94     else (
95       let out = shout "timeout 120 koji buildinfo %s 2>&1 ||:" verrel in
96       if no_such_build out then
97         `No_such_build, None
98       else (
99         let state =
100           try
101             let subs = Pcre.exec ~rex:state out in
102             match Pcre.get_substring subs 1 with
103             | "BUILDING" -> `Building
104             | "COMPLETE" -> `Complete
105             | "DELETED" -> `Deleted
106             | "FAILED" -> `Failed
107             | "CANCELED" -> `Canceled
108             | sub ->
109               failwith (sprintf "koji_build_state_task: %s: unknown build state '%s'"
110                           verrel sub)
111           with
112             Not_found ->
113               failwith (sprintf "koji_build_state_task: %s: no build state found"
114                           verrel) in
115         let task =
116           try
117             let subs = Pcre.exec ~rex:task_id out in
118             Some (int_of_string (Pcre.get_substring subs 1))
119           with
120             Not_found -> None in
121
122         if state == `Complete then
123           memory_set key "1";
124
125         state, task
126       )
127     )
128
129 (* Perform a Koji build and wait until it finishes.  If it fails,
130  * throw an exception.
131  *)
132 let koji_build =
133   let created_task = Pcre.regexp "Created task: (\\d+)" in
134   let name_or_service_not_known =
135     contains_substring "Name or service not known" in
136   let completed_successfully = contains_substring "completed successfully" in
137   let failed = contains_substring "FAILED" in
138   fun ?(wait = true) ?side_tag pkg branch ->
139     let repodir = fedora_repo pkg branch in
140     let out =
141       shout "
142         cd %s
143         fedpkg build%s%s 2>&1
144       " repodir
145         (if not wait then " --nowait" else "")
146         (match side_tag with None -> "" | Some t -> " --target " ^ t)
147     in
148     if not wait then (
149       (* Just check the task was created. *)
150       if not (Pcre.pmatch ~rex:created_task out) then (
151         failwith "fedpkg build: build failed to start"
152       )
153     ) else (
154       let task_id =
155         try
156           let subs = Pcre.exec ~rex:created_task out in
157           int_of_string (Pcre.get_substring subs 1)
158         with Not_found ->
159           failwith "could not find task ID in fedpkg build output" in
160       let rec loop out =
161         if name_or_service_not_known out then (
162           let out =
163             shout "cd %s && koji watch-task %d 2>&1 ||:" repodir task_id in
164           loop out
165         )
166         else if completed_successfully out then
167           ()
168         else if failed out then (
169           failwith "koji build failed"
170         )
171         else
172           failwith (sprintf "koji_build: unknown output: %s" out)
173       in
174       loop out
175     )
176
177 let koji_wait_repo =
178   let successfully_waited = contains_substring "Successfully waited" in
179   fun target verrel ->
180     let out = shout "koji wait-repo %s --build=%s 2>&1 ||:" target verrel in
181     if successfully_waited out then
182       ()
183     else
184       failwith (sprintf "koji_wait_repo: unknown output: %s" out)