ci.scm 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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. (define-module (guix ci)
  20. #:use-module (guix http-client)
  21. #:use-module (guix utils)
  22. #:use-module (json)
  23. #:use-module (srfi srfi-1)
  24. #:use-module (ice-9 match)
  25. #:use-module (guix i18n)
  26. #:use-module (guix diagnostics)
  27. #:autoload (guix channels) (channel)
  28. #:export (build-product?
  29. build-product-id
  30. build-product-type
  31. build-product-file-size
  32. build-product-path
  33. build?
  34. build-id
  35. build-derivation
  36. build-evaluation
  37. build-system
  38. build-status
  39. build-timestamp
  40. build-products
  41. checkout?
  42. checkout-commit
  43. checkout-channel
  44. evaluation?
  45. evaluation-id
  46. evaluation-spec
  47. evaluation-complete?
  48. evaluation-checkouts
  49. %query-limit
  50. queued-builds
  51. latest-builds
  52. evaluation
  53. latest-evaluations
  54. evaluations-for-commit
  55. channel-with-substitutes-available))
  56. ;;; Commentary:
  57. ;;;
  58. ;;; This module provides a client to the HTTP interface of the Hydra and
  59. ;;; Cuirass continuous integration (CI) tools.
  60. ;;;
  61. ;;; Code:
  62. (define-json-mapping <build-product> make-build-product
  63. build-product?
  64. json->build-product
  65. (id build-product-id) ;integer
  66. (type build-product-type) ;string
  67. (file-size build-product-file-size) ;integer
  68. (path build-product-path)) ;string
  69. (define-json-mapping <build> make-build build?
  70. json->build
  71. (id build-id "id") ;integer
  72. (derivation build-derivation) ;string | #f
  73. (evaluation build-evaluation) ;integer
  74. (system build-system) ;string
  75. (status build-status "buildstatus" ) ;integer
  76. (timestamp build-timestamp) ;integer
  77. (products build-products "buildproducts" ;<build-product>*
  78. (lambda (products)
  79. (map json->build-product
  80. ;; Before Cuirass 3db603c1, #f is always returned.
  81. (if (vector? products)
  82. (vector->list products)
  83. '())))))
  84. (define-json-mapping <checkout> make-checkout checkout?
  85. json->checkout
  86. (commit checkout-commit) ;string (SHA1)
  87. (channel checkout-channel)) ;string (name)
  88. (define-json-mapping <evaluation> make-evaluation evaluation?
  89. json->evaluation
  90. (id evaluation-id) ;integer
  91. (spec evaluation-spec "specification") ;string
  92. (complete? evaluation-complete? "status"
  93. (match-lambda
  94. (0 #t)
  95. (_ #f))) ;Boolean
  96. (checkouts evaluation-checkouts "checkouts" ;<checkout>*
  97. (lambda (checkouts)
  98. (map json->checkout
  99. (vector->list checkouts)))))
  100. (define %query-limit
  101. ;; Max number of builds requested in queries.
  102. 1000)
  103. (define (json-fetch url)
  104. (let* ((port (http-fetch url))
  105. (json (json->scm port)))
  106. (close-port port)
  107. json))
  108. (define* (queued-builds url #:optional (limit %query-limit))
  109. "Return the list of queued derivations on URL."
  110. (let ((queue (json-fetch (string-append url "/api/queue?nr="
  111. (number->string limit)))))
  112. (map json->build (vector->list queue))))
  113. (define* (latest-builds url #:optional (limit %query-limit)
  114. #:key evaluation system job status)
  115. "Return the latest builds performed by the CI server at URL. If EVALUATION
  116. is an integer, restrict to builds of EVALUATION. If SYSTEM is true (a system
  117. string such as \"x86_64-linux\"), restrict to builds for SYSTEM."
  118. (define* (option name value #:optional (->string identity))
  119. (if value
  120. (string-append "&" name "=" (->string value))
  121. ""))
  122. (let ((latest (json-fetch (string-append url "/api/latestbuilds?nr="
  123. (number->string limit)
  124. (option "evaluation" evaluation
  125. number->string)
  126. (option "system" system)
  127. (option "job" job)
  128. (option "status" status
  129. number->string)))))
  130. ;; Note: Hydra does not provide a "derivation" field for entries in
  131. ;; 'latestbuilds', but Cuirass does.
  132. (map json->build (vector->list latest))))
  133. (define (evaluation url evaluation)
  134. "Return the given EVALUATION performed by the CI server at URL."
  135. (let ((evaluation (json-fetch
  136. (string-append url "/api/evaluation?id="
  137. (number->string evaluation)))))
  138. (json->evaluation evaluation)))
  139. (define* (latest-evaluations url
  140. #:optional (limit %query-limit)
  141. #:key spec)
  142. "Return the latest evaluations performed by the CI server at URL. If SPEC
  143. is passed, only consider the evaluations for the given SPEC specification."
  144. (let ((spec (if spec
  145. (format #f "&spec=~a" spec)
  146. "")))
  147. (map json->evaluation
  148. (vector->list
  149. (json->scm
  150. (http-fetch
  151. (string-append url "/api/evaluations?nr="
  152. (number->string limit)
  153. spec)))))))
  154. (define* (evaluations-for-commit url commit #:optional (limit %query-limit))
  155. "Return the evaluations among the latest LIMIT evaluations that have COMMIT
  156. as one of their inputs."
  157. (filter (lambda (evaluation)
  158. (find (lambda (checkout)
  159. (string=? (checkout-commit checkout) commit))
  160. (evaluation-checkouts evaluation)))
  161. (latest-evaluations url limit)))
  162. (define (find-latest-commit-with-substitutes url)
  163. "Return the latest commit with available substitutes for the Guix package
  164. definitions at URL. Return false if no commit were found."
  165. (let* ((job-name (string-append "guix." (%current-system)))
  166. (build (match (latest-builds url 1
  167. #:job job-name
  168. #:status 0) ;success
  169. ((build) build)
  170. (_ #f)))
  171. (evaluation (and build
  172. (evaluation url (build-evaluation build))))
  173. (commit (and evaluation
  174. (match (evaluation-checkouts evaluation)
  175. ((checkout)
  176. (checkout-commit checkout))))))
  177. commit))
  178. (define (channel-with-substitutes-available chan url)
  179. "Return a channel inheriting from CHAN but which commit field is set to the
  180. latest commit with available substitutes for the Guix package definitions at
  181. URL. The current system is taken into account.
  182. If no commit with available substitutes were found, the commit field is set to
  183. false and a warning message is printed."
  184. (let ((commit (find-latest-commit-with-substitutes url)))
  185. (unless commit
  186. (warning (G_ "could not find available substitutes at ~a~%")
  187. url))
  188. (channel
  189. (inherit chan)
  190. (commit commit))))