summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2025-07-01 17:36:01 +0200
committerLudovic Courtès <ludo@gnu.org>2025-07-04 18:41:43 +0200
commit6b42df3ad65e4aadb4e848eed8f36eea1974391a (patch)
tree7ff0fe65f2120916f40933e529bc632ed1371cb3
parent821e517ea4cfb1a5b0e7ef16e756cfce3e6fa92d (diff)
services: ci: Add Forgejo Runner service.
* gnu/services/ci.scm (<forgejo-runner-configuration>): New record type. (create-forgejo-runner-account, forgejo-runner-activation) (write-yaml, yaml-file, forgejo-runner-shepherd-service): New procedures. (forgejo-runner-service-type): New variable. * doc/guix.texi (Continuous Integration): Add “Forgejo Runner” heading. Co-authored-by: David Thompson <davet@gnu.org> Change-Id: Iba42d84da35812afa60e94773fbbadd68eca9813
-rw-r--r--doc/guix.texi107
-rw-r--r--gnu/services/ci.scm195
2 files changed, 301 insertions, 1 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index a9f64bd9e42..d48dabfc10f 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -37870,6 +37870,113 @@ Base URL to use for links to laminar itself.
37870@end table 37870@end table
37871@end deftp 37871@end deftp
37872 37872
37873@subsubheading Forgejo Runner
37874
37875@cindex continuous integration, Forgejo
37876@cindex Forgejo, continuous integration
37877The @code{(gnu services ci)} also provides a service for
37878@uref{https://code.forgejo.org/forgejo/runner, Forgejo Runner}, a daemon
37879that connects to an instance of the @uref{https://forgejo.org, Forgejo
37880code collaboration tool} and runs jobs for continuous integration.
37881
37882A minimal configuration mostly with default values that can be added to
37883the @code{services} field of your operating system looks like this:
37884
37885@lisp
37886(service forgejo-runner-service-type
37887 (forgejo-runner-configuration
37888 (name "my-runner")
37889 (labels '("guix" "linux"))))
37890@end lisp
37891
37892This provides a @code{forgejo-runner} Shepherd service. That service
37893will initially fail to start; you will have to manually @dfn{register}
37894the runner against the Forgejo server by running a command like:
37895
37896@example
37897herd register forgejo-runner @var{url} @var{token}
37898@end example
37899
37900@noindent
37901... where the arguments are as follows:
37902
37903@table @var
37904@item url
37905the URL of the Forgejo server---e.g.,
37906@indicateurl{https://codeberg.org};
37907@item token
37908an access token
37909@uref{https://forgejo.org/docs/latest/admin/runner-installation/#standard-registration,
37910provided by the Forgejo server}.
37911@end table
37912
37913Once registration has succeeded, you can start the runner:
37914
37915@example
37916herd enable forgejo-runner
37917herd start forgejo-runner
37918@end example
37919
37920The runner then receives orders from the Forgejo server to execute
37921@dfn{actions}. Actions are commands and workflows specified by YAML
37922files in the @file{.forgejo/workflows} directory of source code
37923repositories---see @uref{https://forgejo.org/docs/v7.0/user/actions/,
37924the Forgejo Action documentation} for more info.
37925
37926Note that at the moment @code{forgejo-runner-service-type} lets you run
37927only one runner. Details about the configuration of this service
37928follow.
37929
37930@defvar forgejo-runner-service-type
37931This is the service type for Forgejo Runner. Its value must be a
37932@code{forgejo-runner-configuration} record, documented below.
37933@end defvar
37934
37935@deftp {Data Type} forgejo-runner-configuration
37936This data type represents the configuration of an instance of
37937@code{forgejo-runner-service-type}. It contains the following fields:
37938
37939@table @asis
37940@item @code{package} (default: @code{forgejo-runner})
37941The Forgejo Runner package to use.
37942
37943@item @code{name} (default: @code{#~(gethostname)})
37944Name of the runner as will be shown in the runner management interface
37945of Forgejo.
37946
37947@item @code{labels} (default: @code{'("guix")})
37948List of
37949@uref{https://forgejo.org/docs/latest/admin/actions/#choosing-labels,
37950labels} representing the type of environment the runner provides and
37951that actions may refer to.
37952
37953@item @code{capacity} (default: @code{1})
37954Number of tasks to be executed concurrently.
37955
37956@item @code{timeout} (default: @code{(* 3 3600)})
37957Maximum duration of a job, in seconds.
37958
37959@item @code{fetch-timeout} (default: @code{5})
37960Maximum duration for fetching the job from the Forgejo server, in
37961seconds.
37962
37963@item @code{fetch-interval} (default: @code{2})
37964Interval (in seconds) for fetching the job from the Forgejo server.
37965
37966@item @code{report-interval} (default: @code{1})
37967Interval (in seconds) for reporting the job status and log to the
37968Forgejo server.
37969
37970@item @code{data-directory} (default: @code{"/var/lib/forgejo-runner"})
37971Directory where @command{forgejo-runner} will store persistent data such
37972as its configuration and access token.
37973
37974@item @code{run-directory} (default: @code{"/var/run/forgejo-runner"})
37975Directory where @command{forgejo-runner} stores cached data.
37976
37977@end table
37978@end deftp
37979
37873@node Power Management Services 37980@node Power Management Services
37874@subsection Power Management Services 37981@subsection Power Management Services
37875 37982
diff --git a/gnu/services/ci.scm b/gnu/services/ci.scm
index d0363595a20..595cad347e6 100644
--- a/gnu/services/ci.scm
+++ b/gnu/services/ci.scm
@@ -1,6 +1,8 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2018, 2019, 2020, 2021 Christopher Baines <mail@cbaines.net> 2;;; Copyright © 2018, 2019, 2020, 2021 Christopher Baines <mail@cbaines.net>
3;;; Copyright © 2021, 2022 Arun Isaac <arunisaac@systemreboot.net> 3;;; Copyright © 2021, 2022 Arun Isaac <arunisaac@systemreboot.net>
4;;; Copyright © 2025 David Thompson <davet@gnu.org>
5;;; Copyright © 2025 Ludovic Courtès <ludo@gnu.org>
4;;; 6;;;
5;;; This file is part of GNU Guix. 7;;; This file is part of GNU Guix.
6;;; 8;;;
@@ -20,6 +22,7 @@
20(define-module (gnu services ci) 22(define-module (gnu services ci)
21 #:use-module (guix gexp) 23 #:use-module (guix gexp)
22 #:use-module (guix records) 24 #:use-module (guix records)
25 #:autoload (guix modules) (source-module-closure)
23 #:use-module (gnu packages admin) 26 #:use-module (gnu packages admin)
24 #:use-module (gnu packages ci) 27 #:use-module (gnu packages ci)
25 #:use-module (gnu services) 28 #:use-module (gnu services)
@@ -39,7 +42,22 @@
39 laminar-configuration-archive-url 42 laminar-configuration-archive-url
40 laminar-configuration-base-url 43 laminar-configuration-base-url
41 44
42 laminar-service-type)) 45 laminar-service-type
46
47 forgejo-runner-configuration
48 forgejo-runner-configuration?
49 forgejo-runner-configuration-package
50 forgejo-runner-configuration-data-directory
51 forgejo-runner-configuration-run-directory
52 forgejo-runner-configuration-name
53 forgejo-runner-configuration-labels
54 forgejo-runner-configuration-capacity
55 forgejo-runner-configuration-timeout
56 forgejo-runner-configuration-fetch-timeout
57 forgejo-runner-configuration-fetch-interval
58 forgejo-runner-configuration-report-interval
59
60 forgejo-runner-service-type))
43 61
44;;;; Commentary: 62;;;; Commentary:
45;;; 63;;;
@@ -146,3 +164,178 @@
146 (default-value (laminar-configuration)) 164 (default-value (laminar-configuration))
147 (description 165 (description
148 "Run the Laminar continuous integration service."))) 166 "Run the Laminar continuous integration service.")))
167
168
169;;;
170;;; Forgejo runner.
171;;;
172
173(define-record-type* <forgejo-runner-configuration>
174 forgejo-runner-configuration
175 make-forgejo-runner-configuration
176 forgejo-runner-configuration?
177 (package forgejo-runner-configuration-package
178 (default forgejo-runner))
179 (data-directory forgejo-runner-configuration-data-directory
180 (default "/var/lib/forgejo-runner"))
181 (run-directory forgejo-runner-configuration-run-directory
182 (default "/var/run/forgejo-runner"))
183
184 ;; Configuration options for the YAML config file:
185 ;; <https://forgejo.org/docs/latest/admin/runner-installation/#configuration>.
186 (name forgejo-runner-configuration-name
187 (default #~(gethostname)))
188 (labels forgejo-runner-configuration-labels
189 (default '("guix")))
190 (capacity forgejo-runner-configuration-job-capacity
191 (default 1))
192 (timeout forgejo-runner-configuration-timeout
193 (default (* 3 3600)))
194 (fetch-timeout forgejo-runner-configuration-fetch-timeout
195 (default 5))
196 (fetch-interval forgejo-runner-configuration-fetch-interval
197 (default 2))
198 (report-interval forgejo-runner-configuration-report-interval
199 (default 1)))
200
201(define (create-forgejo-runner-account config)
202 (list (user-account
203 (name "forgejo-runner")
204 (group "forgejo-runner")
205 (system? #t)
206 (comment "Forgejo Runner user")
207 (home-directory
208 (forgejo-runner-configuration-data-directory config)))
209 (user-group
210 (name "forgejo-runner")
211 (system? #t))))
212
213(define (forgejo-runner-activation config)
214 (match-record config <forgejo-runner-configuration>
215 (data-directory run-directory)
216 #~(let* ((user (getpwnam "forgejo-runner")))
217 (mkdir-p #$run-directory)
218 (chown #$run-directory (passwd:uid user) (passwd:gid user)))))
219
220;; Very naive YAML writer that does just enough for our needs.
221(define* (write-yaml port exp depth)
222 (match exp
223 ((? string? str)
224 (write str port))
225 ((? number? n)
226 (display n port))
227 (('seconds (? number? n))
228 (format port "~as" n))
229 (#(values ...)
230 (display "[ " port)
231 (let ((strings
232 (map (lambda (value)
233 (call-with-output-string
234 (lambda (port)
235 (write-yaml port value depth))))
236 values)))
237 (display (string-join strings ", ") port))
238 (display " ]" port))
239 (() (values))
240 ((((? symbol? k) . v) . rest)
241 (do ((i 0 (1+ i)))
242 ((= i depth))
243 (display " " port))
244 (display k port)
245 (display ": " port)
246 (match v
247 (((k* . v*) . _) ; subtree
248 (newline port)
249 (write-yaml port v (1+ depth)))
250 (_ (write-yaml port v depth)))
251 (newline port)
252 (write-yaml port rest depth))))
253
254(define (yaml-file name exp)
255 (plain-file name
256 (call-with-output-string
257 (lambda (port)
258 (write-yaml port exp 0)))))
259
260(define (forgejo-runner-shepherd-service config)
261 (match-record config <forgejo-runner-configuration>
262 (package data-directory run-directory name
263 capacity timeout fetch-timeout fetch-interval report-interval
264 labels)
265 (define runner (file-append package "/bin/forgejo-runner"))
266 (define runner-file (string-append data-directory "/runner"))
267 (define config
268 (yaml-file
269 "forgejo-runner-config.yml"
270 `((runner . ((file . ,runner-file)
271 (capacity . ,capacity)
272 (timeout . (seconds ,timeout))
273 (fetch_timeout . (seconds ,fetch-timeout))
274 (fetch_interval . (seconds ,fetch-interval))
275 (report_interval . (seconds ,report-interval))
276 (labels . ,(list->vector labels))))
277 (cache . ((dir . ,(string-append run-directory "/cache"))))
278 (host . ((workdir_parent
279 . ,(string-append run-directory "/act")))))))
280
281 (list (shepherd-service
282 (provision '(forgejo-runner))
283 (requirement '(user-processes networking))
284 (start #~(make-forkexec-constructor
285 (list #$runner "daemon" "--config" #$config)
286 #:user "forgejo-runner"
287 #:group "forgejo-runner"
288 #:directory #$run-directory
289 #:environment-variables
290 ;; Provide access to a fresh Guix obtained via 'guix
291 ;; pull'.
292 (cons* (string-append "PATH="
293 #$data-directory "/.config/guix/current/bin"
294 ":/run/current-system/profile/bin")
295 (string-append "HOME=" #$data-directory)
296 "GIT_SSL_CAINFO=/etc/ssl/certs/ca-certificates.crt"
297 (default-environment-variables))))
298 (stop #~(make-kill-destructor))
299 (actions
300 (list
301 (shepherd-configuration-action config)
302 (shepherd-action
303 (procedure
304 #~(lambda (running instance token)
305 (define status
306 (spawn-command (list #$runner "register"
307 "--no-interactive"
308 "--config" #$config
309 "--name" #$name
310 "--instance" instance
311 "--token" token)
312 #:user "forgejo-runner"
313 #:group "forgejo-runner"))
314
315 (if (zero? status)
316 (format #t "Successfully registered runner \
317'~a' for '~a'.~%"
318 #$name instance)
319 (format #t "'~a register' failed with status ~a.~%"
320 #$runner status))
321 (zero? status)))
322 (name 'register)
323 (documentation "Register this runner with a Forgejo server.
324This action takes two arguments: the Forgejo server URL and an access
325token."))))
326 (documentation "Forgejo task runner")))))
327
328(define forgejo-runner-service-type
329 (service-type
330 (name 'forgejo-runner)
331 (extensions
332 (list (service-extension activation-service-type
333 forgejo-runner-activation)
334 (service-extension account-service-type
335 create-forgejo-runner-account)
336 (service-extension shepherd-root-service-type
337 forgejo-runner-shepherd-service)))
338 (default-value (forgejo-runner-configuration))
339 (description
340 "Run @command{forgejo-runner}, a daemon to run tasks for the Forgejo
341source code collaboration service.")))