projects/guix·blob javascript-team · refs

363 lines · 15.0 KiB

1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2018, 2019, 2020, 2021 Christopher Baines <[email protected]>
3;;; Copyright © 2021, 2022 Arun Isaac <[email protected]>
4;;; Copyright © 2025 David Thompson <[email protected]>
5;;; Copyright © 2025 Ludovic Courtès <[email protected]>
6;;; Copyright © 2026 Igorj Gorjaĉev <[email protected]>
7;;;
8;;; This file is part of GNU Guix.
9;;;
10;;; GNU Guix is free software; you can redistribute it and/or modify
11;;; it under the terms of the GNU General Public License as published by
12;;; the Free Software Foundation, either version 3 of the License, or
13;;; (at your option) any later version.
14;;;
15;;; GNU Guix is distributed in the hope that it will be useful,
16;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;;; GNU General Public License for more details.
19;;;
20;;; You should have received a copy of the GNU General Public License
21;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22
23;;;; Commentary:
24;;;
25;;; This module implements a service that to run instances of Laminar, a
26;;; continuous integration tool.
27;;;
28;;;; Code:
29
30(define-module (gnu services ci)
31 #:use-module (guix gexp)
32 #:use-module (guix records)
33 #:autoload (guix modules) (source-module-closure)
34 #:use-module (gnu packages admin)
35 #:use-module (gnu packages ci)
36 #:use-module (gnu services)
37 #:use-module (gnu services base)
38 #:use-module (gnu services shepherd)
39 #:use-module (gnu services admin)
40 #:use-module (gnu system shadow)
41 #:use-module (ice-9 match)
42 #:export (laminar-configuration
43 laminar-configuration?
44 laminar-configuration-home-directory
45 laminar-configuration-supplementary-groups
46 laminar-configuration-bind-http
47 laminar-configuration-bind-rpc
48 laminar-configuration-title
49 laminar-configuration-keep-rundirs
50 laminar-configuration-archive-url
51 laminar-configuration-base-url
52
53 laminar-service-type
54
55 forgejo-runner-configuration
56 forgejo-runner-configuration?
57 forgejo-runner-configuration-package
58 forgejo-runner-configuration-extra-shepherd-requirement
59 forgejo-runner-configuration-user
60 forgejo-runner-configuration-group
61 forgejo-runner-configuration-supplementary-groups
62 forgejo-runner-configuration-data-directory
63 forgejo-runner-configuration-run-directory
64 forgejo-runner-configuration-name
65 forgejo-runner-configuration-labels
66 forgejo-runner-configuration-capacity
67 forgejo-runner-configuration-timeout
68 forgejo-runner-configuration-fetch-timeout
69 forgejo-runner-configuration-fetch-interval
70 forgejo-runner-configuration-report-interval
71
72 forgejo-runner-service-type))
73
74(define-record-type* <laminar-configuration>
75 laminar-configuration make-laminar-configuration
76 laminar-configuration?
77 (laminar laminars-configuration-laminar
78 (default laminar))
79 (home-directory laminar-configuration-home-directory
80 (default "/var/lib/laminar"))
81 (supplementary-groups laminar-configuration-supplementary-groups
82 (default '()))
83 (bind-http laminar-configuration-bind-http
84 (default "*:8080"))
85 (bind-rpc laminar-configuration-bind-rpc
86 (default "unix-abstract:laminar"))
87 (title laminar-configuration-title
88 (default "Laminar"))
89 (keep-rundirs laminar-keep-rundirs
90 (default 0))
91 (archive-url laminar-archive-url
92 (default #f))
93 (base-url laminar-base-url
94 (default #f)))
95
96(define laminar-shepherd-service
97 (match-lambda
98 (($ <laminar-configuration> laminar home-directory supplementary-groups
99 bind-http bind-rpc
100 title keep-rundirs archive-url
101 base-url)
102 (list (shepherd-service
103 (documentation "Run Laminar.")
104 (provision '(laminar))
105 (requirement '(user-processes networking))
106 (start #~(make-forkexec-constructor
107 (list #$(file-append laminar "/sbin/laminard"))
108 #:environment-variables
109 `(,(string-append "LAMINAR_HOME="
110 #$home-directory)
111 ,(string-append "LAMINAR_BIND_HTTP="
112 #$bind-http)
113 ,(string-append "LAMINAR_BIND_RPC="
114 #$bind-rpc)
115 ,(string-append "LAMINAR_TITLE="
116 #$title)
117 ,(string-append "LAMINAR_KEEP_RUNDIRS="
118 #$(number->string
119 keep-rundirs))
120 ,@(if #$archive-url
121 (list
122 (string-append "LAMINAR_ARCHIVE_URL="
123 #$archive-url))
124 '())
125 ,@(if #$base-url
126 (list
127 (string-append "LAMINAR_BASE_URL="
128 #$base-url))
129 '()))
130 #:user "laminar"
131 #:group "laminar"
132 #:supplementary-groups '#$supplementary-groups))
133 (stop #~(make-kill-destructor)))))))
134
135(define (laminar-account config)
136 "Return the user accounts and user groups for CONFIG."
137 (list (user-group
138 (name "laminar")
139 (system? #t))
140 (user-account
141 (name "laminar")
142 (group "laminar")
143 (supplementary-groups
144 (laminar-configuration-supplementary-groups config))
145 (system? #t)
146 (comment "Laminar privilege separation user")
147 (home-directory (laminar-configuration-home-directory config))
148 (shell #~(string-append #$shadow "/sbin/nologin")))))
149
150(define (laminar-activation config)
151 (let ((bind-http (laminar-configuration-bind-http config)))
152 #~(begin
153 ;; If listen is a unix socket, create its parent directory.
154 (when (string-prefix? "unix:" #$bind-http)
155 (let ((run-directory
156 (dirname (substring #$bind-http (string-length "unix:"))))
157 (user (getpw "laminar")))
158 (mkdir-p run-directory)
159 (chown run-directory (passwd:uid user) (passwd:gid user)))))))
160
161(define laminar-service-type
162 (service-type
163 (name 'laminar)
164 (extensions
165 (list
166 (service-extension shepherd-root-service-type laminar-shepherd-service)
167 (service-extension account-service-type laminar-account)
168 (service-extension activation-service-type laminar-activation)))
169 (default-value (laminar-configuration))
170 (description
171 "Run the Laminar continuous integration service.")))
172
173
174;;;
175;;; Forgejo runner.
176;;;
177
178(define-record-type* <forgejo-runner-configuration>
179 forgejo-runner-configuration
180 make-forgejo-runner-configuration
181 forgejo-runner-configuration?
182 (package forgejo-runner-configuration-package
183 (default forgejo-runner))
184 (extra-shepherd-requirement
185 forgejo-runner-configuration-extra-shepherd-requirement
186 (default '(dockerd)))
187 (user forgejo-runner-configuration-user
188 (default "forgejo-runner"))
189 (group forgejo-runner-configuration-group
190 (default "forgejo-runner"))
191 (supplementary-groups
192 forgejo-runner-configuration-supplementary-groups
193 (default '("docker")))
194 (data-directory forgejo-runner-configuration-data-directory
195 (default "/var/lib/forgejo-runner"))
196 (run-directory forgejo-runner-configuration-run-directory
197 (default "/var/run/forgejo-runner"))
198 (log-file forgejo-runner-configuration-log-file
199 (default "/var/log/forgejo-runner.log"))
200
201 ;; Configuration options for the YAML config file:
202 ;; <https://forgejo.org/docs/latest/admin/runner-installation/#configuration>.
203 (name forgejo-runner-configuration-name
204 (default #~(gethostname)))
205 (labels forgejo-runner-configuration-labels
206 (default '("guix")))
207 (capacity forgejo-runner-configuration-job-capacity
208 (default 1))
209 (timeout forgejo-runner-configuration-timeout
210 (default (* 3 3600)))
211 (fetch-timeout forgejo-runner-configuration-fetch-timeout
212 (default 5))
213 (fetch-interval forgejo-runner-configuration-fetch-interval
214 (default 2))
215 (report-interval forgejo-runner-configuration-report-interval
216 (default 1)))
217
218(define (create-forgejo-runner-account config)
219 (list (user-account
220 (name (forgejo-runner-configuration-user config))
221 (group (forgejo-runner-configuration-group config))
222 (supplementary-groups
223 (forgejo-runner-configuration-supplementary-groups config))
224 (system? #t)
225 (comment "Forgejo Runner user")
226 (home-directory
227 (forgejo-runner-configuration-data-directory config)))
228 (user-group
229 (name (forgejo-runner-configuration-group config))
230 (system? #t))))
231
232(define (forgejo-runner-activation config)
233 (match-record config <forgejo-runner-configuration>
234 (user data-directory run-directory)
235 #~(let* ((user (getpwnam #$user)))
236 (mkdir-p #$run-directory)
237 (chown #$run-directory (passwd:uid user) (passwd:gid user)))))
238
239;; Very naive YAML writer that does just enough for our needs.
240(define* (write-yaml port exp depth)
241 (match exp
242 ((? string? str)
243 (write str port))
244 ((? number? n)
245 (display n port))
246 (('seconds (? number? n))
247 (format port "~as" n))
248 (#(values ...)
249 (display "[ " port)
250 (let ((strings
251 (map (lambda (value)
252 (call-with-output-string
253 (lambda (port)
254 (write-yaml port value depth))))
255 values)))
256 (display (string-join strings ", ") port))
257 (display " ]" port))
258 (() (values))
259 ((((? symbol? k) . v) . rest)
260 (do ((i 0 (1+ i)))
261 ((= i depth))
262 (display " " port))
263 (display k port)
264 (display ": " port)
265 (match v
266 (((k* . v*) . _) ; subtree
267 (newline port)
268 (write-yaml port v (1+ depth)))
269 (_ (write-yaml port v depth)))
270 (newline port)
271 (write-yaml port rest depth))))
272
273(define (yaml-file name exp)
274 (plain-file name
275 (call-with-output-string
276 (lambda (port)
277 (write-yaml port exp 0)))))
278
279(define (forgejo-runner-shepherd-service config)
280 (match-record config <forgejo-runner-configuration>
281 (package extra-shepherd-requirement user group supplementary-groups
282 data-directory run-directory log-file name capacity
283 timeout fetch-timeout fetch-interval report-interval
284 labels)
285 (define runner (file-append package "/bin/forgejo-runner"))
286 (define runner-file (string-append data-directory "/runner"))
287 (define config
288 (yaml-file
289 "forgejo-runner-config.yml"
290 `((runner . ((file . ,runner-file)
291 (capacity . ,capacity)
292 (timeout . (seconds ,timeout))
293 (fetch_timeout . (seconds ,fetch-timeout))
294 (fetch_interval . (seconds ,fetch-interval))
295 (report_interval . (seconds ,report-interval))
296 (labels . ,(list->vector labels))))
297 (cache . ((dir . ,(string-append run-directory "/cache"))))
298 (host . ((workdir_parent
299 . ,(string-append run-directory "/act")))))))
300
301 (list (shepherd-service
302 (provision '(forgejo-runner))
303 (requirement `(user-processes networking ,@extra-shepherd-requirement))
304 (start #~(make-forkexec-constructor
305 (list #$runner "daemon" "--config" #$config)
306 #:user #$user
307 #:group #$group
308 #:supplementary-groups '#$supplementary-groups
309 #:directory #$run-directory
310 #:log-file #$log-file
311 #:environment-variables
312 ;; Provide access to a fresh Guix obtained via 'guix
313 ;; pull'.
314 (cons* (string-append "PATH="
315 #$data-directory "/.config/guix/current/bin"
316 ":/run/current-system/profile/bin")
317 (string-append "HOME=" #$data-directory)
318 "GIT_SSL_CAINFO=/etc/ssl/certs/ca-certificates.crt"
319 (default-environment-variables))))
320 (stop #~(make-kill-destructor))
321 (actions
322 (list
323 (shepherd-configuration-action config)
324 (shepherd-action
325 (procedure
326 #~(lambda (running instance token)
327 (define status
328 (spawn-command (list #$runner "register"
329 "--no-interactive"
330 "--config" #$config
331 "--name" #$name
332 "--instance" instance
333 "--token" token)
334 #:user #$user
335 #:group #$group))
336
337 (if (zero? status)
338 (format #t "Successfully registered runner \
339'~a' for '~a'.~%"
340 #$name instance)
341 (format #t "'~a register' failed with status ~a.~%"
342 #$runner status))
343 (zero? status)))
344 (name 'register)
345 (documentation "Register this runner with a Forgejo server.
346This action takes two arguments: the Forgejo server URL and an access
347token."))))
348 (documentation "Forgejo task runner")))))
349
350(define forgejo-runner-service-type
351 (service-type
352 (name 'forgejo-runner)
353 (extensions
354 (list (service-extension activation-service-type
355 forgejo-runner-activation)
356 (service-extension account-service-type
357 create-forgejo-runner-account)
358 (service-extension shepherd-root-service-type
359 forgejo-runner-shepherd-service)))
360 (default-value (forgejo-runner-configuration))
361 (description
362 "Run @command{forgejo-runner}, a daemon to run tasks for the Forgejo
363source code collaboration service.")))