diff options
| author | Julien Lepiller <julien@lepiller.eu> | 2021-08-05 03:46:40 +0200 |
|---|---|---|
| committer | Julien Lepiller <julien@lepiller.eu> | 2021-09-02 22:56:55 +0200 |
| commit | c60daa8e9dfa5f641ecb5f4f3a9135e27b58d2d7 (patch) | |
| tree | 70f8f431c5ccd7a04f43255b2b7ab079f661bd45 | |
| parent | cc16103861b26836908a7d16e0751739a0e20da2 (diff) | |
gnu: version-control: Add gitile service.
* gnu/services/version-control.scm (gitile-service-type): New variable.
* doc/guix.texi (Version Control Services): Document it.
* gnu/tests/version-control.scm (%test-gitile): New variable.
| -rw-r--r-- | doc/guix.texi | 132 | ||||
| -rw-r--r-- | gnu/services/version-control.scm | 128 | ||||
| -rw-r--r-- | gnu/tests/version-control.scm | 138 |
3 files changed, 395 insertions, 3 deletions
diff --git a/doc/guix.texi b/doc/guix.texi index ab178a6b068..36a0c7f5ec7 100644 --- a/doc/guix.texi +++ b/doc/guix.texi | |||
| @@ -25144,6 +25144,7 @@ of strings and G-expressions. | |||
| 25144 | @end table | 25144 | @end table |
| 25145 | @end deffn | 25145 | @end deffn |
| 25146 | 25146 | ||
| 25147 | @anchor{NGINX} | ||
| 25147 | @subsubheading NGINX | 25148 | @subsubheading NGINX |
| 25148 | 25149 | ||
| 25149 | @deffn {Scheme Variable} nginx-service-type | 25150 | @deffn {Scheme Variable} nginx-service-type |
| @@ -31544,6 +31545,137 @@ This setting controls the commands and features to enable within Gitolite. | |||
| 31544 | @end deftp | 31545 | @end deftp |
| 31545 | 31546 | ||
| 31546 | 31547 | ||
| 31548 | @subsubheading Gitile Service | ||
| 31549 | |||
| 31550 | @cindex Gitile service | ||
| 31551 | @cindex Git, forge | ||
| 31552 | @uref{https://git.lepiller.eu/gitile, Gitile} is a Git forge for viewing | ||
| 31553 | public git repository contents from a web browser. | ||
| 31554 | |||
| 31555 | Gitile works best in collaboration with Gitolite, and will serve the public | ||
| 31556 | repositories from Gitolite by default. The service should listen only on | ||
| 31557 | a local port, and a webserver should be configured to serve static resources. | ||
| 31558 | The gitile service provides an easy way to extend the Nginx service for | ||
| 31559 | that purpose (@pxref{NGINX}). | ||
| 31560 | |||
| 31561 | The following example will configure Gitile to serve repositories from a | ||
| 31562 | custom location, with some default messages for the home page and the | ||
| 31563 | footers. | ||
| 31564 | |||
| 31565 | @lisp | ||
| 31566 | (service gitile-service-type | ||
| 31567 | (gitile-configuration | ||
| 31568 | (repositories "/srv/git") | ||
| 31569 | (base-git-url "https://myweb.site/git") | ||
| 31570 | (index-title "My git repositories") | ||
| 31571 | (intro '((p "This is all my public work!"))) | ||
| 31572 | (footer '((p "This is the end"))) | ||
| 31573 | (nginx-server-block | ||
| 31574 | (nginx-server-configuration | ||
| 31575 | (ssl-certificate | ||
| 31576 | "/etc/letsencrypt/live/myweb.site/fullchain.pem") | ||
| 31577 | (ssl-certificate-key | ||
| 31578 | "/etc/letsencrypt/live/myweb.site/privkey.pem") | ||
| 31579 | (listen '("443 ssl http2" "[::]:443 ssl http2")) | ||
| 31580 | (locations | ||
| 31581 | (list | ||
| 31582 | ;; Allow for https anonymous fetch on /git/ urls. | ||
| 31583 | (git-http-nginx-location-configuration | ||
| 31584 | (git-http-configuration | ||
| 31585 | (uri-path "/git/") | ||
| 31586 | (git-root "/var/lib/gitolite/repositories"))))))))) | ||
| 31587 | @end lisp | ||
| 31588 | |||
| 31589 | In addition to the configuration record, you should configure your git | ||
| 31590 | repositories to contain some optional information. First, your public | ||
| 31591 | repositories need to contain the @file{git-daemon-export-ok} magic file | ||
| 31592 | that allows Git to export the repository. Gitile uses the presence of this | ||
| 31593 | file to detect public repositories it should make accessible. To do so with | ||
| 31594 | Gitolite for instance, modify your @file{conf/gitolite.conf} to include | ||
| 31595 | this in the repositories you want to make public: | ||
| 31596 | |||
| 31597 | @example | ||
| 31598 | repo foo | ||
| 31599 | R = daemon | ||
| 31600 | @end example | ||
| 31601 | |||
| 31602 | In addition, Gitile can read the repository configuration to display more | ||
| 31603 | infomation on the repository. Gitile uses the gitweb namespace for its | ||
| 31604 | configuration. As an example, you can use the following in your | ||
| 31605 | @file{conf/gitolite.conf}: | ||
| 31606 | |||
| 31607 | @example | ||
| 31608 | repo foo | ||
| 31609 | R = daemon | ||
| 31610 | desc = A long description, optionally with <i>HTML</i>, shown on the index page | ||
| 31611 | config gitweb.name = The Foo Project | ||
| 31612 | config gitweb.synopsis = A short description, shown on the main page of the project | ||
| 31613 | @end example | ||
| 31614 | |||
| 31615 | Do not forget to commit and push these changes once you are satisfied. You | ||
| 31616 | may need to change your gitolite configuration to allow the previous | ||
| 31617 | configuration options to be set. One way to do that is to add the | ||
| 31618 | following service definition: | ||
| 31619 | |||
| 31620 | @lisp | ||
| 31621 | (service gitolite-service-type | ||
| 31622 | (gitolite-configuration | ||
| 31623 | (admin-pubkey (local-file "key.pub")) | ||
| 31624 | (rc-file | ||
| 31625 | (gitolite-rc-file | ||
| 31626 | (umask #o0027) | ||
| 31627 | ;; Allow to set any configuration key | ||
| 31628 | (git-config-keys ".*") | ||
| 31629 | ;; Allow any text as a valid configuration value | ||
| 31630 | (unsafe-patt "^$"))))) | ||
| 31631 | @end lisp | ||
| 31632 | |||
| 31633 | @deftp {Data Type} gitile-configuration | ||
| 31634 | Data type representing the configuration for @code{gitile-service-type}. | ||
| 31635 | |||
| 31636 | @table @asis | ||
| 31637 | @item @code{package} (default: @var{gitile}) | ||
| 31638 | Gitile package to use. | ||
| 31639 | |||
| 31640 | @item @code{host} (default: @code{"localhost"}) | ||
| 31641 | The host on which gitile is listening. | ||
| 31642 | |||
| 31643 | @item @code{port} (default: @code{8080}) | ||
| 31644 | The port on which gitile is listening. | ||
| 31645 | |||
| 31646 | @item @code{database} (default: @code{"/var/lib/gitile/gitile-db.sql"}) | ||
| 31647 | The location of the database. | ||
| 31648 | |||
| 31649 | @item @code{repositories} (default: @code{"/var/lib/gitolite/repositories"}) | ||
| 31650 | The location of the repositories. Note that only public repositories will | ||
| 31651 | be shown by Gitile. To make a repository public, add an empty | ||
| 31652 | @file{git-daemon-export-ok} file at the root of that repository. | ||
| 31653 | |||
| 31654 | @item @code{base-git-url} | ||
| 31655 | The base git url that will be used to show clone commands. | ||
| 31656 | |||
| 31657 | @item @code{index-title} (default: @code{"Index"}) | ||
| 31658 | The page title for the index page that lists all the available repositories. | ||
| 31659 | |||
| 31660 | @item @code{intro} (default: @code{'()}) | ||
| 31661 | The intro content, as a list of sxml expressions. This is shown above the list | ||
| 31662 | of repositories, on the index page. | ||
| 31663 | |||
| 31664 | @item @code{footer} (default: @code{'()}) | ||
| 31665 | The footer content, as a list of sxml expressions. This is shown on every | ||
| 31666 | page served by Gitile. | ||
| 31667 | |||
| 31668 | @item @code{nginx-server-block} | ||
| 31669 | An nginx server block that will be extended and used as a reverse proxy by | ||
| 31670 | Gitile to serve its pages, and as a normal web server to serve its assets. | ||
| 31671 | |||
| 31672 | You can use this block to add more custom URLs to your domain, such as a | ||
| 31673 | @code{/git/} URL for anonymous clones, or serving any other files you would | ||
| 31674 | like to serve. | ||
| 31675 | @end table | ||
| 31676 | @end deftp | ||
| 31677 | |||
| 31678 | |||
| 31547 | @node Game Services | 31679 | @node Game Services |
| 31548 | @subsection Game Services | 31680 | @subsection Game Services |
| 31549 | 31681 | ||
diff --git a/gnu/services/version-control.scm b/gnu/services/version-control.scm index ab86f82e620..3315e80c6ff 100644 --- a/gnu/services/version-control.scm +++ b/gnu/services/version-control.scm | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | ;;; Copyright © 2017 Oleg Pykhalov <go.wigust@gmail.com> | 4 | ;;; Copyright © 2017 Oleg Pykhalov <go.wigust@gmail.com> |
| 5 | ;;; Copyright © 2017 Clément Lassieur <clement@lassieur.org> | 5 | ;;; Copyright © 2017 Clément Lassieur <clement@lassieur.org> |
| 6 | ;;; Copyright © 2018 Christopher Baines <mail@cbaines.net> | 6 | ;;; Copyright © 2018 Christopher Baines <mail@cbaines.net> |
| 7 | ;;; Copyright © 2021 Julien Lepiller <julien@lepiller.eu> | ||
| 7 | ;;; | 8 | ;;; |
| 8 | ;;; This file is part of GNU Guix. | 9 | ;;; This file is part of GNU Guix. |
| 9 | ;;; | 10 | ;;; |
| @@ -59,7 +60,21 @@ | |||
| 59 | gitolite-rc-file-roles | 60 | gitolite-rc-file-roles |
| 60 | gitolite-rc-file-enable | 61 | gitolite-rc-file-enable |
| 61 | 62 | ||
| 62 | gitolite-service-type)) | 63 | gitolite-service-type |
| 64 | |||
| 65 | gitile-configuration | ||
| 66 | gitile-configuration-package | ||
| 67 | gitile-configuration-host | ||
| 68 | gitile-configuration-port | ||
| 69 | gitile-configuration-database | ||
| 70 | gitile-configuration-repositories | ||
| 71 | gitile-configuration-git-base-url | ||
| 72 | gitile-configuration-index-title | ||
| 73 | gitile-configuration-intro | ||
| 74 | gitile-configuration-footer | ||
| 75 | gitile-configuration-nginx | ||
| 76 | |||
| 77 | gitile-service-type)) | ||
| 63 | 78 | ||
| 64 | ;;; Commentary: | 79 | ;;; Commentary: |
| 65 | ;;; | 80 | ;;; |
| @@ -386,3 +401,114 @@ access to exported repositories under @file{/srv/git}." | |||
| 386 | By default, the @code{git} user is used, but this is configurable. | 401 | By default, the @code{git} user is used, but this is configurable. |
| 387 | Additionally, Gitolite can integrate with with tools like gitweb or cgit to | 402 | Additionally, Gitolite can integrate with with tools like gitweb or cgit to |
| 388 | provide a web interface to view selected repositories."))) | 403 | provide a web interface to view selected repositories."))) |
| 404 | |||
| 405 | ;;; | ||
| 406 | ;;; Gitile | ||
| 407 | ;;; | ||
| 408 | |||
| 409 | (define-record-type* <gitile-configuration> | ||
| 410 | gitile-configuration make-gitile-configuration gitile-configuration? | ||
| 411 | (package gitile-configuration-package | ||
| 412 | (default gitile)) | ||
| 413 | (host gitile-configuration-host | ||
| 414 | (default "127.0.0.1")) | ||
| 415 | (port gitile-configuration-port | ||
| 416 | (default 8080)) | ||
| 417 | (database gitile-configuration-database | ||
| 418 | (default "/var/lib/gitile/gitile-db.sql")) | ||
| 419 | (repositories gitile-configuration-repositories | ||
| 420 | (default "/var/lib/gitolite/repositories")) | ||
| 421 | (base-git-url gitile-configuration-base-git-url) | ||
| 422 | (index-title gitile-configuration-index-title | ||
| 423 | (default "Index")) | ||
| 424 | (intro gitile-configuration-intro | ||
| 425 | (default '())) | ||
| 426 | (footer gitile-configuration-footer | ||
| 427 | (default '())) | ||
| 428 | (nginx gitile-configuration-nginx)) | ||
| 429 | |||
| 430 | (define (gitile-config-file host port database repositories base-git-url | ||
| 431 | index-title intro footer) | ||
| 432 | (define build | ||
| 433 | #~(write `(config | ||
| 434 | (port #$port) | ||
| 435 | (host #$host) | ||
| 436 | (database #$database) | ||
| 437 | (repositories #$repositories) | ||
| 438 | (base-git-url #$base-git-url) | ||
| 439 | (index-title #$index-title) | ||
| 440 | (intro #$intro) | ||
| 441 | (footer #$footer)) | ||
| 442 | (open-output-file #$output))) | ||
| 443 | |||
| 444 | (computed-file "gitile.conf" build)) | ||
| 445 | |||
| 446 | (define gitile-nginx-server-block | ||
| 447 | (match-lambda | ||
| 448 | (($ <gitile-configuration> package host port database repositories | ||
| 449 | base-git-url index-title intro footer nginx) | ||
| 450 | (list (nginx-server-configuration | ||
| 451 | (inherit nginx) | ||
| 452 | (locations | ||
| 453 | (append | ||
| 454 | (list | ||
| 455 | (nginx-location-configuration | ||
| 456 | (uri "/") | ||
| 457 | (body | ||
| 458 | (list | ||
| 459 | #~(string-append "proxy_pass http://" #$host | ||
| 460 | ":" (number->string #$port) | ||
| 461 | "/;"))))) | ||
| 462 | (map | ||
| 463 | (lambda (loc) | ||
| 464 | (nginx-location-configuration | ||
| 465 | (uri loc) | ||
| 466 | (body | ||
| 467 | (list | ||
| 468 | #~(string-append "root " #$package "/share/gitile/assets;"))))) | ||
| 469 | '("/css" "/js" "/images")) | ||
| 470 | (nginx-server-configuration-locations nginx)))))))) | ||
| 471 | |||
| 472 | (define gitile-shepherd-service | ||
| 473 | (match-lambda | ||
| 474 | (($ <gitile-configuration> package host port database repositories | ||
| 475 | base-git-url index-title intro footer nginx) | ||
| 476 | (list (shepherd-service | ||
| 477 | (provision '(gitile)) | ||
| 478 | (requirement '(loopback)) | ||
| 479 | (documentation "gitile") | ||
| 480 | (start (let ((gitile (file-append package "/bin/gitile"))) | ||
| 481 | #~(make-forkexec-constructor | ||
| 482 | `(,#$gitile "-c" #$(gitile-config-file | ||
| 483 | host port database | ||
| 484 | repositories | ||
| 485 | base-git-url index-title | ||
| 486 | intro footer)) | ||
| 487 | #:user "gitile" | ||
| 488 | #:group "git"))) | ||
| 489 | (stop #~(make-kill-destructor))))))) | ||
| 490 | |||
| 491 | (define %gitile-accounts | ||
| 492 | (list (user-group | ||
| 493 | (name "git") | ||
| 494 | (system? #t)) | ||
| 495 | (user-account | ||
| 496 | (name "gitile") | ||
| 497 | (group "git") | ||
| 498 | (system? #t) | ||
| 499 | (comment "Gitile user") | ||
| 500 | (home-directory "/var/empty") | ||
| 501 | (shell (file-append shadow "/sbin/nologin"))))) | ||
| 502 | |||
| 503 | (define gitile-service-type | ||
| 504 | (service-type | ||
| 505 | (name 'gitile) | ||
| 506 | (description "Run Gitile, a small Git forge. Expose public repositories | ||
| 507 | on the web.") | ||
| 508 | (extensions | ||
| 509 | (list (service-extension account-service-type | ||
| 510 | (const %gitile-accounts)) | ||
| 511 | (service-extension shepherd-root-service-type | ||
| 512 | gitile-shepherd-service) | ||
| 513 | (service-extension nginx-service-type | ||
| 514 | gitile-nginx-server-block))))) | ||
diff --git a/gnu/tests/version-control.scm b/gnu/tests/version-control.scm index d3cf19c9138..a7cde1f1632 100644 --- a/gnu/tests/version-control.scm +++ b/gnu/tests/version-control.scm | |||
| @@ -38,7 +38,8 @@ | |||
| 38 | #:use-module (guix modules) | 38 | #:use-module (guix modules) |
| 39 | #:export (%test-cgit | 39 | #:export (%test-cgit |
| 40 | %test-git-http | 40 | %test-git-http |
| 41 | %test-gitolite)) | 41 | %test-gitolite |
| 42 | %test-gitile)) | ||
| 42 | 43 | ||
| 43 | (define README-contents | 44 | (define README-contents |
| 44 | "Hello! This is what goes inside the 'README' file.") | 45 | "Hello! This is what goes inside the 'README' file.") |
| @@ -63,7 +64,10 @@ | |||
| 63 | (invoke git "commit" "-m" "That's a commit.")) | 64 | (invoke git "commit" "-m" "That's a commit.")) |
| 64 | 65 | ||
| 65 | (mkdir-p "/srv/git") | 66 | (mkdir-p "/srv/git") |
| 66 | (rename-file "/tmp/test-repo/.git" "/srv/git/test"))))) | 67 | (rename-file "/tmp/test-repo/.git" "/srv/git/test") |
| 68 | (with-output-to-file "/srv/git/test/git-daemon-export-ok" | ||
| 69 | (lambda _ | ||
| 70 | (display ""))))))) | ||
| 67 | 71 | ||
| 68 | (define %test-repository-service | 72 | (define %test-repository-service |
| 69 | ;; Service that creates /srv/git/test. | 73 | ;; Service that creates /srv/git/test. |
| @@ -416,3 +420,133 @@ HTTP-PORT." | |||
| 416 | (name "gitolite") | 420 | (name "gitolite") |
| 417 | (description "Clone the Gitolite admin repository.") | 421 | (description "Clone the Gitolite admin repository.") |
| 418 | (value (run-gitolite-test)))) | 422 | (value (run-gitolite-test)))) |
| 423 | |||
| 424 | ;;; | ||
| 425 | ;;; Gitile. | ||
| 426 | ;;; | ||
| 427 | |||
| 428 | (define %gitile-configuration-nginx | ||
| 429 | (nginx-server-configuration | ||
| 430 | (root "/does/not/exists") | ||
| 431 | (try-files (list "$uri" "=404")) | ||
| 432 | (listen '("19418")) | ||
| 433 | (ssl-certificate #f) | ||
| 434 | (ssl-certificate-key #f))) | ||
| 435 | |||
| 436 | (define %gitile-os | ||
| 437 | ;; Operating system under test. | ||
| 438 | (simple-operating-system | ||
| 439 | (service dhcp-client-service-type) | ||
| 440 | (simple-service 'srv-git activation-service-type | ||
| 441 | #~(mkdir-p "/srv/git")) | ||
| 442 | (service gitile-service-type | ||
| 443 | (gitile-configuration | ||
| 444 | (base-git-url "http://localhost") | ||
| 445 | (repositories "/srv/git") | ||
| 446 | (nginx %gitile-configuration-nginx))) | ||
| 447 | %test-repository-service)) | ||
| 448 | |||
| 449 | (define* (run-gitile-test #:optional (http-port 19418)) | ||
| 450 | "Run tests in %GITOLITE-OS, which has nginx running and listening on | ||
| 451 | HTTP-PORT." | ||
| 452 | (define os | ||
| 453 | (marionette-operating-system | ||
| 454 | %gitile-os | ||
| 455 | #:imported-modules '((gnu services herd) | ||
| 456 | (guix combinators)))) | ||
| 457 | |||
| 458 | (define vm | ||
| 459 | (virtual-machine | ||
| 460 | (operating-system os) | ||
| 461 | (port-forwardings `((8081 . ,http-port))))) | ||
| 462 | |||
| 463 | (define test | ||
| 464 | (with-imported-modules '((gnu build marionette)) | ||
| 465 | #~(begin | ||
| 466 | (use-modules (srfi srfi-11) (srfi srfi-64) | ||
| 467 | (gnu build marionette) | ||
| 468 | (web uri) | ||
| 469 | (web client) | ||
| 470 | (web response)) | ||
| 471 | |||
| 472 | (define marionette | ||
| 473 | (make-marionette (list #$vm))) | ||
| 474 | |||
| 475 | (mkdir #$output) | ||
| 476 | (chdir #$output) | ||
| 477 | |||
| 478 | (test-begin "gitile") | ||
| 479 | |||
| 480 | ;; XXX: Shepherd reads the config file *before* binding its control | ||
| 481 | ;; socket, so /var/run/shepherd/socket might not exist yet when the | ||
| 482 | ;; 'marionette' service is started. | ||
| 483 | (test-assert "shepherd socket ready" | ||
| 484 | (marionette-eval | ||
| 485 | `(begin | ||
| 486 | (use-modules (gnu services herd)) | ||
| 487 | (let loop ((i 10)) | ||
| 488 | (cond ((file-exists? (%shepherd-socket-file)) | ||
| 489 | #t) | ||
| 490 | ((> i 0) | ||
| 491 | (sleep 1) | ||
| 492 | (loop (- i 1))) | ||
| 493 | (else | ||
| 494 | 'failure)))) | ||
| 495 | marionette)) | ||
| 496 | |||
| 497 | ;; Wait for nginx to be up and running. | ||
| 498 | (test-assert "nginx running" | ||
| 499 | (marionette-eval | ||
| 500 | '(begin | ||
| 501 | (use-modules (gnu services herd)) | ||
| 502 | (start-service 'nginx)) | ||
| 503 | marionette)) | ||
| 504 | |||
| 505 | ;; Make sure the PID file is created. | ||
| 506 | (test-assert "PID file" | ||
| 507 | (marionette-eval | ||
| 508 | '(file-exists? "/var/run/nginx/pid") | ||
| 509 | marionette)) | ||
| 510 | |||
| 511 | ;; Make sure Git test repository is created. | ||
| 512 | (test-assert "Git test repository" | ||
| 513 | (marionette-eval | ||
| 514 | '(file-exists? "/srv/git/test") | ||
| 515 | marionette)) | ||
| 516 | |||
| 517 | (sleep 2) | ||
| 518 | |||
| 519 | ;; Make sure we can access pages that correspond to our repository. | ||
| 520 | (letrec-syntax ((test-url | ||
| 521 | (syntax-rules () | ||
| 522 | ((_ path code) | ||
| 523 | (test-equal (string-append "GET " path) | ||
| 524 | code | ||
| 525 | (let-values (((response body) | ||
| 526 | (http-get (string-append | ||
| 527 | "http://localhost:8081" | ||
| 528 | path)))) | ||
| 529 | (response-code response)))) | ||
| 530 | ((_ path) | ||
| 531 | (test-url path 200))))) | ||
| 532 | (test-url "/") | ||
| 533 | (test-url "/css/gitile.css") | ||
| 534 | (test-url "/test") | ||
| 535 | (test-url "/test/commits") | ||
| 536 | (test-url "/test/tree" 404) | ||
| 537 | (test-url "/test/tree/-") | ||
| 538 | (test-url "/test/tree/-/README") | ||
| 539 | (test-url "/test/does-not-exist" 404) | ||
| 540 | (test-url "/test/tree/-/does-not-exist" 404) | ||
| 541 | (test-url "/does-not-exist" 404)) | ||
| 542 | |||
| 543 | (test-end) | ||
| 544 | (exit (= (test-runner-fail-count (test-runner-current)) 0))))) | ||
| 545 | |||
| 546 | (gexp->derivation "gitile-test" test)) | ||
| 547 | |||
| 548 | (define %test-gitile | ||
| 549 | (system-test | ||
| 550 | (name "gitile") | ||
| 551 | (description "Connect to a running Gitile server.") | ||
| 552 | (value (run-gitile-test)))) | ||
