gnu: tor: Update to 0.4.5.9 [security fixes].
[jackhill/guix/guix.git] / guix / ci.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
3 ;;; Copyright © 2020 Mathieu Othacehe <othacehe@gnu.org>
4 ;;;
5 ;;; This file is part of GNU Guix.
6 ;;;
7 ;;; GNU Guix is free software; you can redistribute it and/or modify it
8 ;;; under the terms of the GNU General Public License as published by
9 ;;; the Free Software Foundation; either version 3 of the License, or (at
10 ;;; your option) any later version.
11 ;;;
12 ;;; GNU Guix is distributed in the hope that it will be useful, but
13 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;;; GNU General Public License for more details.
16 ;;;
17 ;;; You should have received a copy of the GNU General Public License
18 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19
20 (define-module (guix ci)
21 #:use-module (guix http-client)
22 #:use-module (guix utils)
23 #:use-module (json)
24 #:use-module (srfi srfi-1)
25 #:use-module (ice-9 match)
26 #:use-module (guix i18n)
27 #:use-module (guix diagnostics)
28 #:autoload (guix channels) (channel)
29 #:export (build-product?
30 build-product-id
31 build-product-type
32 build-product-file-size
33 build-product-path
34
35 build?
36 build-id
37 build-derivation
38 build-evaluation
39 build-system
40 build-status
41 build-timestamp
42 build-products
43
44 checkout?
45 checkout-commit
46 checkout-channel
47
48 evaluation?
49 evaluation-id
50 evaluation-spec
51 evaluation-complete?
52 evaluation-checkouts
53
54 %query-limit
55 queued-builds
56 latest-builds
57 evaluation
58 latest-evaluations
59 evaluations-for-commit
60
61 channel-with-substitutes-available))
62
63 ;;; Commentary:
64 ;;;
65 ;;; This module provides a client to the HTTP interface of the Hydra and
66 ;;; Cuirass continuous integration (CI) tools.
67 ;;;
68 ;;; Code:
69
70 (define-json-mapping <build-product> make-build-product
71 build-product?
72 json->build-product
73 (id build-product-id) ;integer
74 (type build-product-type) ;string
75 (file-size build-product-file-size) ;integer
76 (path build-product-path)) ;string
77
78 (define-json-mapping <build> make-build build?
79 json->build
80 (id build-id "id") ;integer
81 (derivation build-derivation) ;string | #f
82 (evaluation build-evaluation) ;integer
83 (system build-system) ;string
84 (status build-status "buildstatus" ) ;integer
85 (timestamp build-timestamp) ;integer
86 (products build-products "buildproducts" ;<build-product>*
87 (lambda (products)
88 (map json->build-product
89 ;; Before Cuirass 3db603c1, #f is always returned.
90 (if (vector? products)
91 (vector->list products)
92 '())))))
93
94 (define-json-mapping <checkout> make-checkout checkout?
95 json->checkout
96 (commit checkout-commit) ;string (SHA1)
97 (channel checkout-channel)) ;string (name)
98
99 (define-json-mapping <evaluation> make-evaluation evaluation?
100 json->evaluation
101 (id evaluation-id) ;integer
102 (spec evaluation-spec "specification") ;string
103 (complete? evaluation-complete? "status"
104 (match-lambda
105 (0 #t)
106 (_ #f))) ;Boolean
107 (checkouts evaluation-checkouts "checkouts" ;<checkout>*
108 (lambda (checkouts)
109 (map json->checkout
110 (vector->list checkouts)))))
111
112 (define %query-limit
113 ;; Max number of builds requested in queries.
114 1000)
115
116 (define (json-fetch url)
117 (let* ((port (http-fetch url))
118 (json (json->scm port)))
119 (close-port port)
120 json))
121
122 (define* (queued-builds url #:optional (limit %query-limit))
123 "Return the list of queued derivations on URL."
124 (let ((queue (json-fetch (string-append url "/api/queue?nr="
125 (number->string limit)))))
126 (map json->build (vector->list queue))))
127
128 (define* (latest-builds url #:optional (limit %query-limit)
129 #:key evaluation system job status)
130 "Return the latest builds performed by the CI server at URL. If EVALUATION
131 is an integer, restrict to builds of EVALUATION. If SYSTEM is true (a system
132 string such as \"x86_64-linux\"), restrict to builds for SYSTEM."
133 (define* (option name value #:optional (->string identity))
134 (if value
135 (string-append "&" name "=" (->string value))
136 ""))
137
138 (let ((latest (json-fetch (string-append url "/api/latestbuilds?nr="
139 (number->string limit)
140 (option "evaluation" evaluation
141 number->string)
142 (option "system" system)
143 (option "job" job)
144 (option "status" status
145 number->string)))))
146 ;; Note: Hydra does not provide a "derivation" field for entries in
147 ;; 'latestbuilds', but Cuirass does.
148 (map json->build (vector->list latest))))
149
150 (define (evaluation url evaluation)
151 "Return the given EVALUATION performed by the CI server at URL."
152 (let ((evaluation (json-fetch
153 (string-append url "/api/evaluation?id="
154 (number->string evaluation)))))
155 (json->evaluation evaluation)))
156
157 (define* (latest-evaluations url
158 #:optional (limit %query-limit)
159 #:key spec)
160 "Return the latest evaluations performed by the CI server at URL. If SPEC
161 is passed, only consider the evaluations for the given SPEC specification."
162 (let ((spec (if spec
163 (format #f "&spec=~a" spec)
164 "")))
165 (map json->evaluation
166 (vector->list
167 (json->scm
168 (http-fetch
169 (string-append url "/api/evaluations?nr="
170 (number->string limit)
171 spec)))))))
172
173 (define* (evaluations-for-commit url commit #:optional (limit %query-limit))
174 "Return the evaluations among the latest LIMIT evaluations that have COMMIT
175 as one of their inputs."
176 (filter (lambda (evaluation)
177 (find (lambda (checkout)
178 (string=? (checkout-commit checkout) commit))
179 (evaluation-checkouts evaluation)))
180 (latest-evaluations url limit)))
181
182 (define (find-latest-commit-with-substitutes url)
183 "Return the latest commit with available substitutes for the Guix package
184 definitions at URL. Return false if no commit were found."
185 (let* ((job-name (string-append "guix." (%current-system)))
186 (build (match (latest-builds url 1
187 #:job job-name
188 #:status 0) ;success
189 ((build) build)
190 (_ #f)))
191 (evaluation (and build
192 (evaluation url (build-evaluation build))))
193 (commit (and evaluation
194 (match (evaluation-checkouts evaluation)
195 ((checkout)
196 (checkout-commit checkout))))))
197 commit))
198
199 (define (channel-with-substitutes-available chan url)
200 "Return a channel inheriting from CHAN but which commit field is set to the
201 latest commit with available substitutes for the Guix package definitions at
202 URL. The current system is taken into account.
203
204 If no commit with available substitutes were found, the commit field is set to
205 false and a warning message is printed."
206 (let ((commit (find-latest-commit-with-substitutes url)))
207 (unless commit
208 (warning (G_ "could not find available substitutes at ~a~%")
209 url))
210 (channel
211 (inherit chan)
212 (commit commit))))