1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2018 Oleg Pykhalov <go.wigust@gmail.com>
3 ;;; Copyright © 2018 Clément Lassieur <clement@lassieur.org>
4 ;;; Copyright © 2018 Christopher Baines <mail@cbaines.net>
5 ;;; Copyright © 2019 Arun Isaac <arunisaac@systemreboot.net>
7 ;;; This file is part of GNU Guix.
9 ;;; GNU Guix is free software; you can redistribute it and/or modify it
10 ;;; under the terms of the GNU General Public License as published by
11 ;;; the Free Software Foundation; either version 3 of the License, or (at
12 ;;; your option) any later version.
14 ;;; GNU Guix is distributed in the hope that it will be useful, but
15 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;;; GNU General Public License for more details.
19 ;;; You should have received a copy of the GNU General Public License
20 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22 (define-module (gnu services cgit)
23 #:use-module (gnu packages admin)
24 #:use-module (gnu packages version-control)
25 #:use-module (gnu services base)
26 #:use-module (gnu services configuration)
27 #:use-module (gnu services shepherd)
28 #:use-module (gnu services web)
29 #:use-module (gnu services)
30 #:use-module (gnu system shadow)
31 #:use-module (guix gexp)
32 #:use-module (guix packages)
33 #:use-module (guix records)
34 #:use-module (guix store)
35 #:use-module (ice-9 match)
36 #:use-module (srfi srfi-1)
37 #:use-module (srfi srfi-26)
38 #:export (repository-cgit-configuration
40 %cgit-configuration-nginx
41 cgit-configuration-nginx-config
42 opaque-cgit-configuration
47 ;;; This module provides a service definition for the Cgit a web frontend for
48 ;;; Git repositories written in C.
50 ;;; Note: fields of <cgit-configuration> and <repository-cgit-configuration>
51 ;;; should be specified in the specific order.
55 (define %cgit-configuration-nginx
56 (nginx-server-configuration
60 (nginx-location-configuration
62 (body '("fastcgi_param SCRIPT_FILENAME $document_root/lib/cgit/cgit.cgi;"
63 "fastcgi_param PATH_INFO $uri;"
64 "fastcgi_param QUERY_STRING $args;"
65 "fastcgi_param HTTP_HOST $server_name;"
66 "fastcgi_pass 127.0.0.1:9000;")))))
67 (try-files (list "$uri" "@cgit"))
70 (ssl-certificate-key #f)))
74 ;;; Serialize <cgit-configuration>
77 (define (uglify-field-name field-name)
78 (string-delete #\? (symbol->string field-name)))
80 (define (serialize-field field-name val)
81 #~(format #f "~a=~a\n" #$(uglify-field-name field-name) #$val))
83 (define (serialize-string field-name val)
84 (if (and (string? val) (string=? val ""))
86 (serialize-field field-name val)))
88 (define (serialize-list field-name val)
89 (if (null? val) "" (serialize-field field-name (string-join val))))
91 (define robots-list? list?)
93 (define (serialize-robots-list field-name val)
94 (if (null? val) "" (serialize-field field-name (string-join val ", "))))
96 (define (integer? val)
99 (define (serialize-integer field-name val)
100 (serialize-field field-name (number->string val)))
102 (define (serialize-boolean field-name val)
103 (serialize-integer field-name (if val 1 0)))
105 (define (serialize-repository-cgit-configuration x)
106 (serialize-configuration x repository-cgit-configuration-fields))
108 (define (repository-cgit-configuration-list? val)
111 (define (serialize-repository-cgit-configuration-list field-name val)
113 #$@(map serialize-repository-cgit-configuration val)))
115 (define (file-object? val)
116 (or (file-like? val) (string? val)))
117 (define (serialize-file-object field-name val)
118 (serialize-string field-name val))
120 (define (project-list? val)
126 ;;; Serialize <nginx-server-configuration>
129 (define (nginx-server-configuration-list? val)
130 (and (list? val) (and-map nginx-server-configuration? val)))
132 (define (serialize-nginx-server-configuration-list field-name val)
137 ;;; Serialize <repository-cgit-configuration>
140 (define (serialize-repo-field field-name val)
141 #~(format #f "repo.~a=~a\n" #$(uglify-field-name field-name) #$val))
143 (define (serialize-repo-list field-name val)
144 (if (null? val) "" (serialize-repo-field field-name (string-join val))))
146 (define repo-boolean? boolean?)
148 (define (serialize-repo-integer field-name val)
149 (serialize-repo-field field-name (number->string val)))
151 (define (serialize-repo-boolean field-name val)
152 (serialize-repo-integer field-name (if val 1 0)))
153 (define-maybe repo-boolean)
155 (define repo-list? list?)
157 (define repo-string? string?)
159 (define (serialize-repo-string field-name val)
160 (if (string=? val "") "" (serialize-repo-field field-name val)))
162 (define repo-file-object? file-object?)
163 (define serialize-repo-file-object serialize-repo-string)
165 (define module-link-path? list?)
167 (define (serialize-module-link-path field-name val)
171 (format #f "repo.module-link.~a=~a\n" path text)))))
173 (define (serialize-project-list _ val)
177 (if (file-object? val)
179 (plain-file "project-list" (string-join val "\n"))))))
181 (define (serialize-extra-options extra-options)
182 (string-join extra-options "\n" 'suffix))
184 (define repository-directory? string?)
186 (define (serialize-repository-directory _ val)
187 (if (string=? val "") "" (format #f "scan-path=~a\n" val)))
189 (define mimetype-alist? list?)
191 (define (serialize-mimetype-alist field-name val)
192 (format #f "# Mimetypes\n~a"
195 ((extension mimetype)
196 (format #f "mimetype.~a=~a"
197 (symbol->string extension) mimetype)))
200 (define-configuration repository-cgit-configuration
203 "A mask of snapshot formats for this repo that cgit generates links for,
204 restricted by the global @code{snapshots} setting.")
206 (repo-file-object "")
207 "Override the default @code{source-filter}.")
210 "The relative URL used to access the repository.")
212 (repo-file-object "")
213 "Override the default @code{about-filter}.")
216 "Flag which, when set to @samp{age}, enables date ordering in the branch
217 ref list, and when set to @samp{name} enables ordering by branch name.")
220 "A list of URLs which can be used to clone repo.")
222 (repo-file-object "")
223 "Override the default @code{commit-filter}.")
226 "Flag which, when set to @samp{date}, enables strict date ordering in the
227 commit log, and when set to @samp{topo} enables strict topological ordering.")
230 "The name of the default branch for this repository. If no such branch
231 exists in the repository, the first branch name (when sorted) is used as
232 default instead. By default branch pointed to by HEAD, or \"master\" if there
233 is no suitable HEAD.")
236 "The value to show as repository description.")
239 "The value to show as repository homepage.")
241 (repo-file-object "")
242 "Override the default @code{email-filter}.")
243 (enable-commit-graph?
244 (maybe-repo-boolean 'disabled)
245 "A flag which can be used to disable the global setting
246 @code{enable-commit-graph?}.")
247 (enable-log-filecount?
248 (maybe-repo-boolean 'disabled)
249 "A flag which can be used to disable the global setting
250 @code{enable-log-filecount?}.")
251 (enable-log-linecount?
252 (maybe-repo-boolean 'disabled)
253 "A flag which can be used to disable the global setting
254 @code{enable-log-linecount?}.")
255 (enable-remote-branches?
256 (maybe-repo-boolean 'disabled)
257 "Flag which, when set to @code{#t}, will make cgit display remote
258 branches in the summary and refs views.")
259 (enable-subject-links?
260 (maybe-repo-boolean 'disabled)
261 "A flag which can be used to override the global setting
262 @code{enable-subject-links?}.")
263 (enable-html-serving?
264 (maybe-repo-boolean 'disabled)
265 "A flag which can be used to override the global setting
266 @code{enable-html-serving?}.")
269 "Flag which, when set to @code{#t}, hides the repository from the
273 "Flag which, when set to @samp{#t}, ignores the repository.")
275 (repo-file-object "")
276 "URL which specifies the source of an image which will be used as a
277 logo on this repo’s pages.")
280 "URL loaded when clicking on the cgit logo image.")
282 (repo-file-object "")
283 "Override the default @code{owner-filter}.")
286 "Text which will be used as the formatstring for a hyperlink when a
287 submodule is printed in a directory listing. The arguments for the
288 formatstring are the path and SHA1 of the submodule commit.")
290 (module-link-path '())
291 "Text which will be used as the formatstring for a hyperlink when a
292 submodule with the specified subdirectory path is printed in a directory
296 "Override the default maximum statistics period.")
299 "The value to show as repository name.")
302 "A value used to identify the owner of the repository.")
305 "An absolute path to the repository directory.")
308 "A path (relative to repo) which specifies a file to include verbatim
309 as the \"About\" page for this repo.")
312 "The name of the current repository section - all repositories defined
313 after this option will inherit the current section name.")
316 "Extra options will be appended to cgitrc file."))
318 ;; Generate a <cgit-configuration> record, which may include a list of
319 ;; <repository-cgit-configuration>, <nginx-server-configuration>, <package>.
320 (define-configuration cgit-configuration
325 (nginx-server-configuration-list (list %cgit-configuration-nginx))
326 "NGINX configuration.")
329 "Specifies a command which will be invoked to format the content of about
330 pages (both top-level and for each repository).")
333 "Specifies a path, relative to each repository path, which can be used to
334 specify the date and time of the youngest commit in the repository.")
337 "Specifies a command that will be invoked for authenticating repository
341 "Flag which, when set to @samp{age}, enables date ordering in the branch
342 ref list, and when set @samp{name} enables ordering by branch name.")
344 (string "/var/cache/cgit")
345 "Path used to store the cgit cache entries.")
348 "Number which specifies the time-to-live, in minutes, for the cached
349 version of repository pages accessed with a fixed SHA1.")
352 "Number which specifies the time-to-live, in minutes, for the cached
353 version of repository pages accessed without a fixed SHA1.")
356 "Number which specifies the time-to-live, in minutes, for the cached
357 version of the repository summary page.")
360 "Number which specifies the time-to-live, in minutes, for the cached
361 version of the repository index page.")
364 "Number which specifies the time-to-live, in minutes, for the result of
365 scanning a path for Git repositories.")
368 "Number which specifies the time-to-live, in minutes, for the cached
369 version of the repository about page.")
372 "Number which specifies the time-to-live, in minutes, for the cached
373 version of snapshots.")
376 "The maximum number of entries in the cgit cache. When set to
377 @samp{0}, caching is disabled.")
378 (case-sensitive-sort?
380 "Sort items in the repo list case sensitively.")
383 "List of common prefixes which, when combined with a repository URL,
384 generates valid clone URLs for the repository.")
387 "List of @code{clone-url} templates.")
390 "Command which will be invoked to format commit messages.")
393 "Flag which, when set to @samp{date}, enables strict date ordering in the
394 commit log, and when set to @samp{topo} enables strict topological
397 (file-object "/share/cgit/cgit.css")
398 "URL which specifies the css document to include in all cgit pages.")
401 "Specifies a command which will be invoked to format names and email
402 address of committers, authors, and taggers, as represented in various
403 places throughout the cgit interface.")
406 "Flag which, when set to @samp{#t}, will make cgit generate a HTML
407 fragment suitable for embedding in other HTML pages.")
408 (enable-commit-graph?
410 "Flag which, when set to @samp{#t}, will make cgit print an ASCII-art
411 commit history graph to the left of the commit messages in the
412 repository log page.")
413 (enable-filter-overrides?
415 "Flag which, when set to @samp{#t}, allows all filter settings to be
416 overridden in repository-specific cgitrc files.")
417 (enable-follow-links?
419 "Flag which, when set to @samp{#t}, allows users to follow a file in the
423 "If set to @samp{#t}, cgit will act as an dumb HTTP endpoint for Git
427 "Flag which, when set to @samp{#t}, will make cgit generate extra links
428 \"summary\", \"commit\", \"tree\" for each repo in the repository index.")
431 "Flag which, when set to @samp{#t}, will make cgit display the owner of
432 each repo in the repository index.")
433 (enable-log-filecount?
435 "Flag which, when set to @samp{#t}, will make cgit print the number of
436 modified files for each commit on the repository log page.")
437 (enable-log-linecount?
439 "Flag which, when set to @samp{#t}, will make cgit print the number of
440 added and removed lines for each commit on the repository log page.")
441 (enable-remote-branches?
443 "Flag which, when set to @code{#t}, will make cgit display remote
444 branches in the summary and refs views.")
445 (enable-subject-links?
447 "Flag which, when set to @code{1}, will make cgit use the subject of
448 the parent commit as link text when generating links to parent commits
450 (enable-html-serving?
452 "Flag which, when set to @samp{#t}, will make cgit use the subject of the
453 parent commit as link text when generating links to parent commits in
455 (enable-tree-linenumbers?
457 "Flag which, when set to @samp{#t}, will make cgit generate linenumber
458 links for plaintext blobs printed in the tree view.")
461 "Flag which, when set to @samp{#f}, will allow cgit to use Git config to
462 set any repo specific settings.")
464 (file-object "/favicon.ico")
465 "URL used as link to a shortcut icon for cgit.")
468 "The content of the file specified with this option will be included
469 verbatim at the bottom of all pages (i.e. it replaces the standard
470 \"generated by...\" message).")
473 "The content of the file specified with this option will be included
474 verbatim in the HTML HEAD section on all pages.")
477 "The content of the file specified with this option will be included
478 verbatim at the top of all pages.")
481 "Name of a configfile to include before the rest of the current config-
485 "The content of the file specified with this option will be included
486 verbatim above the repository index.")
489 "The content of the file specified with this option will be included
490 verbatim below the heading on the repository index page.")
493 "Flag which, if set to @samp{#t}, makes cgit print commit and tag times
494 in the servers timezone.")
496 (file-object "/share/cgit/cgit.png")
497 "URL which specifies the source of an image which will be used as a logo
501 "URL loaded when clicking on the cgit logo image.")
504 "Command which will be invoked to format the Owner column of the main
508 "Number of items to display in atom feeds view.")
511 "Number of entries to list per page in \"log\" view.")
514 "Number of commit message characters to display in \"log\" view.")
517 "Specifies the number of entries to list per page on the repository index
521 "Specifies the maximum number of repo description characters to display
522 on the repository index page.")
525 "Specifies the maximum size of a blob to display HTML for in KBytes.")
528 "Maximum statistics period. Valid values are @samp{week},@samp{month},
529 @samp{quarter} and @samp{year}.")
531 (mimetype-alist '((gif "image/gif")
535 (pdf "application/pdf")
537 (svg "image/svg+xml")))
538 "Mimetype for the specified filename extension.")
541 "Specifies the file to use for automatic mimetype lookup.")
544 "Text which will be used as the formatstring for a hyperlink when a
545 submodule is printed in a directory listing.")
548 "If set to the value @samp{#t} caching will be disabled.")
551 "If set to @samp{#t} showing full author email addresses will be
555 "Flag which, when set to @samp{#t}, will make cgit omit the standard
556 header on all pages.")
559 "A list of subdirectories inside of @code{repository-directory}, relative
560 to it, that should loaded as Git repositories. An empty list means that all
561 subdirectories will be loaded.")
564 "Text which will be used as default value for @code{cgit-repo-readme}.")
567 "If set to @code{#t} and @code{repository-directory} is enabled, if any
568 repositories are found with a suffix of @code{.git}, this suffix will be
569 removed for the URL and name.")
572 "Maximum number of files to consider when detecting renames.")
575 "The way in which repositories in each section are sorted.")
577 (robots-list (list "noindex" "nofollow"))
578 "Text used as content for the @code{robots} meta-tag.")
580 (string "a fast webinterface for the git dscm")
581 "Text printed below the heading on the repository index page.")
584 "The content of the file specified with this option will be included
585 verbatim below the \"about\" link on the repository index page.")
588 "Text printed as heading on the repository index page.")
591 "If set to @samp{#t} and repository-directory is enabled,
592 repository-directory will recurse into directories whose name starts with a
593 period. Otherwise, repository-directory will stay away from such directories,
594 considered as \"hidden\". Note that this does not apply to the \".git\"
595 directory in non-bare repos.")
598 "Text which specifies the default set of snapshot formats that cgit
599 generates links for.")
600 (repository-directory
601 (repository-directory "/srv/git")
602 "Name of the directory to scan for repositories (represents
606 "The name of the current repository section - all repositories defined
607 after this option will inherit the current section name.")
610 "Flag which, when set to @samp{1}, will sort the sections on the repository
614 "A number which, if defined prior to repository-directory, specifies how
615 many path elements from each repo path to use as a default section name.")
618 "If set to @samp{#t} shows side-by-side diffs instead of unidiffs per
622 "Specifies a command which will be invoked to format plaintext blobs in the
626 "Specifies the number of branches to display in the repository \"summary\"
630 "Specifies the number of log entries to display in the repository
634 "Specifies the number of tags to display in the repository \"summary\"
638 "Filename which, if specified, needs to be present within the repository
639 for cgit to allow access to that repository.")
642 "URL which, if specified, will be used as root for all cgit links.")
644 (repository-cgit-configuration-list '())
645 "A list of @dfn{cgit-repo} records to use with config.")
648 "Extra options will be appended to cgitrc file."))
650 ;; This distinguishes fields whose order matters, and makes sure further
651 ;; changes won't inadvertently change the order.
652 (define (serialize-cgit-configuration config)
653 (define (rest? field)
654 (not (memq (configuration-field-name field)
660 #$(let ((rest (filter rest? cgit-configuration-fields)))
661 (serialize-configuration config rest))
662 #$(serialize-project-list
664 (cgit-configuration-project-list config))
665 #$(serialize-extra-options
666 (cgit-configuration-extra-options config))
667 #$(serialize-repository-directory
668 'repository-directory
669 (cgit-configuration-repository-directory config))
670 #$(serialize-repository-cgit-configuration-list
672 (cgit-configuration-repositories config))))
674 (define-configuration opaque-cgit-configuration
679 (string (configuration-missing-field 'opaque-cgit-configuration 'cgitrc))
680 "The contents of the @code{cgitrc} to use.")
682 (string "/var/cache/cgit")
683 "Path used to store the cgit cache entries.")
685 (nginx-server-configuration-list (list %cgit-configuration-nginx))
686 "NGINX configuration."))
688 (define (cgit-activation config)
689 "Return the activation gexp for CONFIG."
690 (let* ((opaque-config? (opaque-cgit-configuration? config))
693 (opaque-cgit-configuration-cgitrc config)
694 (serialize-cgit-configuration config))))
696 (use-modules (guix build utils))
697 (mkdir-p #$(if opaque-config?
698 (opaque-cgit-configuration-cache-root config)
699 (cgit-configuration-cache-root config)))
700 (copy-file #$(mixed-text-file "cgitrc" config-str)
703 (define (cgit-configuration-nginx-config config)
704 (if (opaque-cgit-configuration? config)
705 (opaque-cgit-configuration-nginx config)
706 (cgit-configuration-nginx config)))
708 (define cgit-service-type
712 (list (service-extension activation-service-type
714 (service-extension nginx-service-type
715 cgit-configuration-nginx-config)
717 ;; Make sure fcgiwrap is instantiated.
718 (service-extension fcgiwrap-service-type
720 (default-value (cgit-configuration))
722 "Run the cgit web interface, which allows users to browse Git
725 (define (generate-cgit-documentation)
726 (generate-documentation
727 `((cgit-configuration
728 ,cgit-configuration-fields
729 (repositories repository-cgit-configuration))
730 (repository-cgit-configuration
731 ,repository-cgit-configuration-fields))
732 'cgit-configuration))