diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2025-07-01 17:36:01 +0200 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2025-07-04 18:41:43 +0200 |
| commit | 6b42df3ad65e4aadb4e848eed8f36eea1974391a (patch) | |
| tree | 7ff0fe65f2120916f40933e529bc632ed1371cb3 | |
| parent | 821e517ea4cfb1a5b0e7ef16e756cfce3e6fa92d (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.texi | 107 | ||||
| -rw-r--r-- | gnu/services/ci.scm | 195 |
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 | ||
| 37877 | The @code{(gnu services ci)} also provides a service for | ||
| 37878 | @uref{https://code.forgejo.org/forgejo/runner, Forgejo Runner}, a daemon | ||
| 37879 | that connects to an instance of the @uref{https://forgejo.org, Forgejo | ||
| 37880 | code collaboration tool} and runs jobs for continuous integration. | ||
| 37881 | |||
| 37882 | A minimal configuration mostly with default values that can be added to | ||
| 37883 | the @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 | |||
| 37892 | This provides a @code{forgejo-runner} Shepherd service. That service | ||
| 37893 | will initially fail to start; you will have to manually @dfn{register} | ||
| 37894 | the runner against the Forgejo server by running a command like: | ||
| 37895 | |||
| 37896 | @example | ||
| 37897 | herd 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 | ||
| 37905 | the URL of the Forgejo server---e.g., | ||
| 37906 | @indicateurl{https://codeberg.org}; | ||
| 37907 | @item token | ||
| 37908 | an access token | ||
| 37909 | @uref{https://forgejo.org/docs/latest/admin/runner-installation/#standard-registration, | ||
| 37910 | provided by the Forgejo server}. | ||
| 37911 | @end table | ||
| 37912 | |||
| 37913 | Once registration has succeeded, you can start the runner: | ||
| 37914 | |||
| 37915 | @example | ||
| 37916 | herd enable forgejo-runner | ||
| 37917 | herd start forgejo-runner | ||
| 37918 | @end example | ||
| 37919 | |||
| 37920 | The runner then receives orders from the Forgejo server to execute | ||
| 37921 | @dfn{actions}. Actions are commands and workflows specified by YAML | ||
| 37922 | files in the @file{.forgejo/workflows} directory of source code | ||
| 37923 | repositories---see @uref{https://forgejo.org/docs/v7.0/user/actions/, | ||
| 37924 | the Forgejo Action documentation} for more info. | ||
| 37925 | |||
| 37926 | Note that at the moment @code{forgejo-runner-service-type} lets you run | ||
| 37927 | only one runner. Details about the configuration of this service | ||
| 37928 | follow. | ||
| 37929 | |||
| 37930 | @defvar forgejo-runner-service-type | ||
| 37931 | This 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 | ||
| 37936 | This 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}) | ||
| 37941 | The Forgejo Runner package to use. | ||
| 37942 | |||
| 37943 | @item @code{name} (default: @code{#~(gethostname)}) | ||
| 37944 | Name of the runner as will be shown in the runner management interface | ||
| 37945 | of Forgejo. | ||
| 37946 | |||
| 37947 | @item @code{labels} (default: @code{'("guix")}) | ||
| 37948 | List of | ||
| 37949 | @uref{https://forgejo.org/docs/latest/admin/actions/#choosing-labels, | ||
| 37950 | labels} representing the type of environment the runner provides and | ||
| 37951 | that actions may refer to. | ||
| 37952 | |||
| 37953 | @item @code{capacity} (default: @code{1}) | ||
| 37954 | Number of tasks to be executed concurrently. | ||
| 37955 | |||
| 37956 | @item @code{timeout} (default: @code{(* 3 3600)}) | ||
| 37957 | Maximum duration of a job, in seconds. | ||
| 37958 | |||
| 37959 | @item @code{fetch-timeout} (default: @code{5}) | ||
| 37960 | Maximum duration for fetching the job from the Forgejo server, in | ||
| 37961 | seconds. | ||
| 37962 | |||
| 37963 | @item @code{fetch-interval} (default: @code{2}) | ||
| 37964 | Interval (in seconds) for fetching the job from the Forgejo server. | ||
| 37965 | |||
| 37966 | @item @code{report-interval} (default: @code{1}) | ||
| 37967 | Interval (in seconds) for reporting the job status and log to the | ||
| 37968 | Forgejo server. | ||
| 37969 | |||
| 37970 | @item @code{data-directory} (default: @code{"/var/lib/forgejo-runner"}) | ||
| 37971 | Directory where @command{forgejo-runner} will store persistent data such | ||
| 37972 | as its configuration and access token. | ||
| 37973 | |||
| 37974 | @item @code{run-directory} (default: @code{"/var/run/forgejo-runner"}) | ||
| 37975 | Directory 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. | ||
| 324 | This action takes two arguments: the Forgejo server URL and an access | ||
| 325 | token.")))) | ||
| 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 | ||
| 341 | source code collaboration service."))) | ||
