build-farm-jobset.el 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. ;;; build-farm-jobset.el --- Interface for jobsets -*- lexical-binding: t -*-
  2. ;; Copyright © 2015–2018 Alex Kost <alezost@gmail.com>
  3. ;; This program is free software; you can redistribute it and/or modify
  4. ;; it under the terms of the GNU General Public License as published by
  5. ;; the Free Software Foundation, either version 3 of the License, or
  6. ;; (at your option) any later version.
  7. ;;
  8. ;; This program is distributed in the hope that it will be useful,
  9. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. ;; GNU General Public License for more details.
  12. ;;
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;; This file provides an interface for displaying jobsets of a build
  17. ;; farm in 'list' and 'info' buffers.
  18. ;; Unlike builds, jobsets for Cuirass and Hydra have very few in common,
  19. ;; so there are 2 different interfaces for these 2 types of jobsets.
  20. ;;; Code:
  21. (require 'cl-lib)
  22. (require 'bui)
  23. (require 'build-farm)
  24. (require 'build-farm-build)
  25. (require 'build-farm-url)
  26. ;;; Common for Hydra and Cuirass
  27. (defun build-farm-jobset-info-insert-url (entry)
  28. "Insert URL for the jobset ENTRY."
  29. (bui-insert-button (build-farm-jobset-url
  30. :root-url (build-farm-current-url)
  31. :jobset-id (bui-entry-id entry))
  32. 'bui-url)
  33. (bui-newline))
  34. ;;; Hydra common
  35. (build-farm-define-entry-type hydra-jobset
  36. :search-types '((project . build-farm-hydra-jobset-api-url))
  37. :filters '(build-farm-hydra-jobset-filter-id)
  38. :filter-names '((nrscheduled . scheduled)
  39. (nrsucceeded . succeeded)
  40. (nrfailed . failed)
  41. (nrtotal . total)))
  42. (defun build-farm-hydra-jobset-id (project jobset)
  43. "Return jobset ID from PROJECT name and JOBSET name."
  44. (concat project "/" jobset))
  45. (defun build-farm-hydra-jobset-filter-id (entry)
  46. "Add ID parameter to Hydra jobset ENTRY."
  47. (cons `(id . ,(build-farm-hydra-jobset-id
  48. (bui-entry-non-void-value entry 'project)
  49. (bui-entry-non-void-value entry 'name)))
  50. entry))
  51. ;;; Hydra Jobset 'info'
  52. (build-farm-define-interface hydra-jobset info
  53. :mode-name "Hydra-Jobset-Info"
  54. :buffer-name "*Farm Jobset Info*"
  55. :format '((name nil (simple bui-info-heading))
  56. nil
  57. build-farm-jobset-info-insert-url
  58. nil
  59. (project format build-farm-hydra-jobset-info-insert-project)
  60. (scheduled format (format build-farm-hydra-jobset-info-scheduled))
  61. (succeeded format (format build-farm-hydra-jobset-info-succeeded))
  62. (failed format (format build-farm-hydra-jobset-info-failed))
  63. (total format (format build-farm-hydra-jobset-info-total))))
  64. (defface build-farm-hydra-jobset-info-scheduled
  65. '((t))
  66. "Face used for the number of scheduled builds."
  67. :group 'build-farm-hydra-jobset-info-faces)
  68. (defface build-farm-hydra-jobset-info-succeeded
  69. '((t :inherit build-farm-build-status-succeeded))
  70. "Face used for the number of succeeded builds."
  71. :group 'build-farm-hydra-jobset-info-faces)
  72. (defface build-farm-hydra-jobset-info-failed
  73. '((t :inherit build-farm-build-status-failed))
  74. "Face used for the number of failed builds."
  75. :group 'build-farm-hydra-jobset-info-faces)
  76. (defface build-farm-hydra-jobset-info-total
  77. '((t))
  78. "Face used for the total number of builds."
  79. :group 'build-farm-hydra-jobset-info-faces)
  80. (defun build-farm-hydra-jobset-info-insert-project (project entry)
  81. "Insert PROJECT button for the jobset ENTRY."
  82. (let ((jobset (bui-entry-non-void-value entry 'name)))
  83. (bui-insert-button project 'build-farm-project)
  84. (bui-insert-indent)
  85. (build-farm-info-insert-builds-button
  86. :project project
  87. :jobset jobset)))
  88. ;;; Hydra Jobset 'list'
  89. (build-farm-define-interface hydra-jobset list
  90. :describe-function 'build-farm-list-describe
  91. :mode-name "Hydra-Jobset-List"
  92. :buffer-name "*Farm Jobsets*"
  93. :format '((name build-farm-hydra-jobset-list-get-name 25 t)
  94. (project nil 10 t)
  95. (scheduled nil 12 t)
  96. (succeeded nil 12 t)
  97. (failed nil 9 t)
  98. (total nil 10 t))
  99. :hint 'build-farm-hydra-jobset-list-hint)
  100. (let ((map build-farm-hydra-jobset-list-mode-map))
  101. (define-key map (kbd "B") 'build-farm-hydra-jobset-list-latest-builds))
  102. (defface build-farm-hydra-jobset-list-status-scheduled
  103. '((t))
  104. "Face used for a jobset name if there are scheduled jobs."
  105. :group 'build-farm-hydra-jobset-list-faces)
  106. (defface build-farm-hydra-jobset-list-status-succeeded
  107. '((t :inherit build-farm-build-status-succeeded))
  108. "Face used for a jobset name if there are no failed or scheduled jobs."
  109. :group 'build-farm-hydra-jobset-list-faces)
  110. (defface build-farm-hydra-jobset-list-status-failed
  111. '((t :inherit build-farm-build-status-failed))
  112. "Face used for a jobset name if there are failed jobs."
  113. :group 'build-farm-hydra-jobset-list-faces)
  114. (defvar build-farm-hydra-jobset-list-default-hint
  115. '(("\\[build-farm-hydra-jobset-list-latest-builds]")
  116. " show latest builds of the current jobset;\n"))
  117. (defun build-farm-hydra-jobset-list-hint ()
  118. "Return hint string for a jobset-list buffer."
  119. (bui-format-hints
  120. build-farm-hydra-jobset-list-default-hint
  121. (bui-default-hint)))
  122. (defun build-farm-hydra-jobset-list-get-name (name entry)
  123. "Return NAME of the jobset ENTRY.
  124. Colorize it with an appropriate face if needed."
  125. (bui-get-string
  126. name
  127. (cond ((> (bui-entry-value entry 'failed) 0)
  128. 'build-farm-hydra-jobset-list-status-failed)
  129. ((> (bui-entry-value entry 'scheduled) 0)
  130. 'build-farm-hydra-jobset-list-status-scheduled)
  131. ((= (bui-entry-value entry 'total)
  132. (bui-entry-value entry 'succeeded))
  133. 'build-farm-hydra-jobset-list-status-succeeded))))
  134. (defun build-farm-hydra-jobset-list-latest-builds (number &rest args)
  135. "Display latest NUMBER of builds of the current jobset.
  136. Interactively, use `build-farm-number-of-builds' variable for
  137. NUMBER. With prefix argument, prompt for it and for the other
  138. ARGS."
  139. (interactive
  140. (let ((entry (bui-list-current-entry)))
  141. (build-farm-build-latest-prompt-args
  142. :project (bui-entry-non-void-value entry 'project)
  143. :jobset (bui-entry-non-void-value entry 'name))))
  144. (apply #'build-farm-get-display
  145. (build-farm-current-url) 'build 'latest number args))
  146. ;;; Cuirass common
  147. (build-farm-define-entry-type cuirass-jobset
  148. :search-types '((all . build-farm-cuirass-jobsets-url))
  149. :filters '(build-farm-cuirass-jobset-filter-id)
  150. :titles '((proc . "Procedure")
  151. (proc-input . "Procedure input")
  152. (proc-file . "Procedure file")
  153. (proc-args . "Procedure arguments")))
  154. (defun build-farm-cuirass-jobset-filter-id (entry)
  155. "Add ID parameter to Cuirass jobset ENTRY if needed."
  156. ;; In the past, Cuirass returned jobset ID but not anymore (is it
  157. ;; temporary?).
  158. (if (bui-void-value? (bui-entry-id entry))
  159. (cons `(id . ,(bui-entry-non-void-value entry 'name))
  160. entry)
  161. entry))
  162. (defface build-farm-cuirass-jobset-file
  163. '((t :inherit bui-file-name))
  164. "Face used for file name of a jobset's procedure."
  165. :group 'build-farm-cuirass-jobset-faces)
  166. (declare-function guix-directory "guix-repl" t)
  167. (defun build-farm-cuirass-jobset-file-action (button)
  168. "Find file of BUTTON.
  169. The BUTTON file name is relative to guix source tree."
  170. (let ((file-name (or (button-get button 'file-name)
  171. (button-label button))))
  172. (if (require 'guix-repl nil t)
  173. (find-file (expand-file-name file-name (guix-directory)))
  174. (error "Sorry, no idea where '%s' is placed :-)"
  175. file-name))))
  176. (define-button-type 'build-farm-cuirass-jobset-file
  177. :supertype 'bui-file
  178. 'face 'build-farm-cuirass-jobset-file
  179. 'action #'build-farm-cuirass-jobset-file-action)
  180. ;;; Cuirass Jobset 'info'
  181. (build-farm-define-interface cuirass-jobset info
  182. :mode-name "Cuirass-Jobset-Info"
  183. :buffer-name "*Farm Jobset Info*"
  184. :format '((name nil (simple bui-info-heading))
  185. nil
  186. build-farm-jobset-info-insert-url
  187. nil
  188. build-farm-cuirass-jobset-info-insert-builds
  189. (load-path-inputs format (format))
  190. (package-path-inputs format (format))
  191. nil
  192. (proc-input format (format))
  193. (proc-file format (format build-farm-cuirass-jobset-file))
  194. (proc format (format))
  195. (proc-args simple
  196. (build-farm-cuirass-jobset-info-insert-proc-args))
  197. (inputs simple (build-farm-cuirass-jobset-info-insert-inputs))))
  198. (bui-define-interface build-farm-cuirass-jobset-args info
  199. :format '((subset format (format))
  200. (systems format (format build-farm-system)))
  201. :reduced? t)
  202. (bui-define-interface build-farm-cuirass-jobset-inputs info
  203. :format '((name format (format))
  204. (url format (format bui-url))
  205. (branch format (format))
  206. (load-path format (format))
  207. (tag format (format))
  208. (commit format (format))
  209. (no-compile? format (format)))
  210. :reduced? t)
  211. (defun build-farm-cuirass-jobset-info-insert-builds (entry)
  212. "Insert 'Builds' button for the jobset ENTRY."
  213. (let ((jobset (bui-entry-non-void-value entry 'name)))
  214. (build-farm-info-insert-builds-button
  215. :jobset jobset))
  216. (bui-newline))
  217. (defun build-farm-cuirass-jobset-info-insert-file (file-name)
  218. "Insert FILE-NAME of a jobset's procedure at point."
  219. (bui-insert-non-nil file-name
  220. (bui-info-insert-value-indent
  221. file-name 'build-farm-jobset-proc-file)))
  222. (defun build-farm-cuirass-jobset-info-insert-proc-args (args)
  223. "Insert procedure ARGS at point."
  224. (bui-newline)
  225. (bui-info-insert-entry args 'build-farm-cuirass-jobset-args 1))
  226. (defun build-farm-cuirass-jobset-info-insert-inputs (inputs)
  227. "Insert jobset INPUTS at point."
  228. (dolist (input inputs)
  229. (bui-newline)
  230. (bui-info-insert-entry input 'build-farm-cuirass-jobset-inputs 1)))
  231. ;;; Cuirass Jobset 'list'
  232. (build-farm-define-interface cuirass-jobset list
  233. :describe-function 'build-farm-list-describe
  234. :mode-name "Cuirass-Jobset-List"
  235. :buffer-name "*Farm Jobsets*"
  236. :hint 'build-farm-cuirass-jobset-list-hint
  237. :format '((name nil 30 t)
  238. (proc-input nil 20 t)
  239. (proc-file build-farm-cuirass-jobset-list-get-file 20 t)))
  240. (let ((map build-farm-cuirass-jobset-list-mode-map))
  241. (define-key map (kbd "B") 'build-farm-cuirass-jobset-list-latest-builds))
  242. (defvar build-farm-cuirass-jobset-list-default-hint
  243. '(("\\[build-farm-cuirass-jobset-list-latest-builds]")
  244. " show latest builds of the current jobset;\n"))
  245. (defun build-farm-cuirass-jobset-list-hint ()
  246. "Return hint string for a jobset-list buffer."
  247. (bui-format-hints
  248. build-farm-cuirass-jobset-list-default-hint
  249. (bui-default-hint)))
  250. (defun build-farm-cuirass-jobset-list-get-file (file-name &optional _)
  251. "Return FILE-NAME button specification for `tabulated-list-entries'."
  252. (bui-get-non-nil file-name
  253. (list file-name
  254. :type 'build-farm-cuirass-jobset-file
  255. 'file-name file-name)))
  256. (defun build-farm-cuirass-jobset-list-latest-builds (number &rest args)
  257. "Display latest NUMBER of builds of the current jobset.
  258. Interactively, use `build-farm-number-of-builds' variable for
  259. NUMBER. With prefix argument, prompt for it and for the other
  260. ARGS."
  261. (interactive
  262. (let ((entry (bui-list-current-entry)))
  263. (build-farm-build-latest-prompt-args
  264. :jobset (bui-entry-non-void-value entry 'name))))
  265. (apply #'build-farm-get-display
  266. (build-farm-current-url) 'build 'latest number args))
  267. ;;; Interactive commands
  268. ;;;###autoload
  269. (defun build-farm-jobsets (&optional project)
  270. "Display jobsets of PROJECT.
  271. PROJECT is required for Hydra build farm and is not needed for
  272. Cuirass."
  273. (interactive
  274. (when (eq 'hydra (build-farm-url-type))
  275. (list (build-farm-read-project))))
  276. (if (eq 'cuirass (build-farm-url-type))
  277. (build-farm-get-display build-farm-url 'cuirass-jobset 'all)
  278. (build-farm-get-display build-farm-url 'hydra-jobset
  279. 'project project)))
  280. ;; Info returned for multiple jobsets (from "api/jobsets") and for a
  281. ;; single jobset (from "jobset") are completely different! Compare:
  282. ;;
  283. ;; (build-farm-receive-data "https://hydra.nixos.org/jobset/hydra/master")
  284. ;; (build-farm-receive-data "https://hydra.nixos.org/api/jobsets?project=hydra")
  285. ;;
  286. ;; How this duality can be supported? Maybe make another
  287. ;; "jobset-configuration" interface? Anyway, `build-farm-jobset'
  288. ;; command is not available yet.
  289. ;; (defun build-farm-jobset (project jobset)
  290. ;; "Display JOBSET of PROJECT."
  291. ;; (interactive (list (build-farm-read-project)
  292. ;; (build-farm-read-jobset project)))
  293. ;; (build-farm-jobset-get-display
  294. ;; 'id (build-farm-jobset-id project jobset)))
  295. (provide 'build-farm-jobset)
  296. ;;; build-farm-jobset.el ends here