From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38154) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1feE3G-0006Hx-3Q for guix-patches@gnu.org; Sat, 14 Jul 2018 02:29:08 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1feE3C-0001u8-Rc for guix-patches@gnu.org; Sat, 14 Jul 2018 02:29:06 -0400 Received: from debbugs.gnu.org ([208.118.235.43]:51533) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1feE3C-0001tx-M1 for guix-patches@gnu.org; Sat, 14 Jul 2018 02:29:02 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1feE3C-0003OR-AR for guix-patches@gnu.org; Sat, 14 Jul 2018 02:29:02 -0400 Subject: [bug#30809] [PATCH 2/2] services: Add Gitolite. Resent-Message-ID: From: Christopher Baines Date: Sat, 14 Jul 2018 07:28:55 +0100 Message-Id: <20180714062855.18705-2-mail@cbaines.net> In-Reply-To: <20180714062855.18705-1-mail@cbaines.net> References: <20180714062855.18705-1-mail@cbaines.net> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+kyle=kyleam.com@gnu.org Sender: "Guix-patches" To: 30809@debbugs.gnu.org * gnu/services/version-control.scm (, ): New record types. (gitolite-accounts, gitolite-setup, gitolite-activation): New procedures. (gitolite-service-type): New variables. * gnu/tests/version-control.scm (%gitolite-test-admin-keypair, %gitolite-os, %test-gitolite): New variables. (run-gitolite-test): New procedure. * doc/guix.texi (Version Control): Document the gitolite service. --- doc/guix.texi | 90 ++++++++++++++++ gnu/services/version-control.scm | 169 ++++++++++++++++++++++++++++++- gnu/tests/version-control.scm | 103 ++++++++++++++++++- 3 files changed, 360 insertions(+), 2 deletions(-) diff --git a/doc/guix.texi b/doc/guix.texi index 7ce364b0a..b43f43bb9 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -20127,6 +20127,96 @@ could instantiate a cgit service like this: (cgitrc ""))) @end example +@subsubheading Gitolite Service + +@cindex Gitolite service +@cindex Git, hosting +@uref{http://gitolite.com/gitolite/, Gitolite} is a tool for hosting Git +repositories on a central server. + +Gitolite can handle multiple repositories and users, and supports flexible +configuration of the permissions for the users on the repositories. + +The following example will configure Gitolite using the default @code{git} +user, and the provided SSH public key. A public key is necessary to setup +Gitolite initially, but can be omitted once Gitolite is setup. + +@example +(service gitolite-service-type + (gitolite-configuration + (admin-pubkey (plain-file + "id_rsa.pub" + "ssh-rsa AAAA... guix@@example.com")))) +@end example + +Gitolite is configured through a special admin repository which you can clone, +for example, if you setup Gitolite on @code{example.com}, you would run the +following command to clone the admin repository. + +@example +git clone git@@example.com:gitolite-admin +@end example + +@deftp {Data Type} gitolite-configuration +Data type representing the configuration for @code{gitolite-service-type}. + +@table @asis +@item @code{package} (default: @var{gitolite}) +Gitolite package to use. + +@item @code{user} (default: @var{git}) +User to use for Gitolite. This will be user that you use when accessing +Gitolite over SSH. + +@item @code{rc-file} (default: @var{(gitolite-rc-file)}) +A ``file-like'' object (@pxref{G-Expressions, file-like objects}), +representing the configuration for Gitolite. + +@item @code{admin-pubkey} (default: @var{#f}) +A ``file-like'' object (@pxref{G-Expressions, file-like objects}) used to +setup Gitolite. This can be omitted once Gitolite has successfully been +setup. + +The following G-exp would use the @file{/home/user/.ssh/id_rsa.pub} file. + +@example +(local-file "/home/user/.ssh/id_rsa.pub") +@end example + +To specify the SSH key as a string, use the @code{plain-file} function. + +@example +(plain-file "id_rsa.pub" "ssh-rsa AAAA... guix@@example.com") +@end example + +@end table +@end deftp + +@deftp {Data Type} gitolite-rc-file +Data type representing the Gitolite RC file. + +@table @asis +@item @code{umask} (default: @code{#o0077}) +This controls the permissions Gitolite sets on the repositories and their +contents. + +A value like @code{#o0027} will give read access to the group used by Gitolite +(by default: @code{git}). This is necessary when using Gitolite with software +like cgit or gitweb. + +@item @code{git-config-keys} (default: @code{""}) +Gitolite allows you to set git config values using the "config" keyword. This +setting allows control over the config keys to accept. + +@item @code{roles} (default: @code{'(("READERS" . 1) ("WRITERS" . ))}) +Set the role names allowed to be used by users running the perms command. + +@item @code{enable} (default: @code{'("help" "desc" "info" "perms" "writable" "ssh-authkeys" "git-config" "daemon" "gitweb")}) +This setting controls the commands and features to enable within Gitolite. + +@end table +@end deftp + @node Game Services @subsubsection Game Services diff --git a/gnu/services/version-control.scm b/gnu/services/version-control.scm index 58274c8be..1000207ed 100644 --- a/gnu/services/version-control.scm +++ b/gnu/services/version-control.scm @@ -40,7 +40,23 @@ git-http-configuration git-http-configuration? - git-http-nginx-location-configuration)) + git-http-nginx-location-configuration + + + gitolite-configuration + gitolite-configuration-package + gitolite-configuration-user + gitolite-configuration-rc-file + gitolite-configuration-admin-pubkey + + + gitolite-rc-file + gitolite-rc-file-umask + gitolite-rc-file-git-config-keys + gitolite-rc-file-roles + gitolite-rc-file-enable + + gitolite-service-type)) ;;; Commentary: ;;; @@ -197,3 +213,154 @@ access to exported repositories under @file{/srv/git}." "") (list "fastcgi_param GIT_PROJECT_ROOT " git-root ";") "fastcgi_param PATH_INFO $1;")))))) + + +;;; +;;; Gitolite +;;; + +(define-record-type* + gitolite-rc-file make-gitolite-rc-file + gitolite-rc-file? + (umask gitolite-rc-file-umask + (default #o0077)) + (git-config-keys gitolite-rc-file-git-config-keys + (default "")) + (roles gitolite-rc-file-roles + (default '(("READERS" . 1) + ("WRITERS" . 1)))) + (enable gitolite-rc-file-enable + (default '("help" + "desc" + "info" + "perms" + "writable" + "ssh-authkeys" + "git-config" + "daemon" + "gitweb")))) + +(define-gexp-compiler (gitolite-rc-file-compiler + (file ) system target) + (match file + (($ umask git-config-keys roles enable) + (apply text-file* "gitolite.rc" + `("%RC = (\n" + " UMASK => " ,(format #f "~4,'0o" umask) ",\n" + " GIT_CONFIG_KEYS => '" ,git-config-keys "',\n" + " ROLES => {\n" + ,@(map (match-lambda + ((role . value) + (simple-format #f " ~A => ~A,\n" role value))) + roles) + " },\n" + "\n" + " ENABLE => [\n" + ,@(map (lambda (value) + (simple-format #f " '~A',\n" value)) + enable) + " ],\n" + ");\n" + "\n" + "1;\n"))))) + +(define-record-type* + gitolite-configuration make-gitolite-configuration + gitolite-configuration? + (package gitolite-configuration-package + (default gitolite)) + (user gitolite-configuration-user + (default "git")) + (rc-file gitolite-configuration-rc-file + (default (gitolite-rc-file))) + (admin-pubkey gitolite-configuration-admin-pubkey + (default #f))) + +(define (gitolite-accounts config) + (let ((user (gitolite-configuration-user config))) + ;; User group and account to run Gitolite. + (list (user-group (name user) (system? #t)) + (user-account + (name user) + (group user) + (system? #t) + (comment "Gitolite user") + (home-directory "/var/lib/gitolite"))))) + +(define gitolite-setup + (match-lambda + (($ package user rc-file admin-pubkey) + #~(begin + (use-modules (ice-9 match) + (guix build utils)) + (if (not (file-exists? "/var/lib/gitolite/.gitolite")) + (let ((user-info (getpwnam #$user))) + (simple-format #t "guix: gitolite: installing ~A\n" + #$rc-file) + (symlink #$rc-file "/var/lib/gitolite/.gitolite.rc") + + ;; The key must be writable, so copy it from the store + (copy-file #$admin-pubkey "/var/lib/gitolite/id_rsa.pub") + + (chmod "/var/lib/gitolite/id_rsa.pub" #o500) + (chown "/var/lib/gitolite/id_rsa.pub" + (passwd:uid user-info) + (passwd:gid user-info)) + + ;; Set the git configuration, to avoid gitolite trying to use + ;; the hostname command, as the network might not be up yet + (with-output-to-file "/var/lib/gitolite/.gitconfig" + (lambda () + (display "[user] + name = GNU Guix + email = guix@localhost +"))) + + (match (primitive-fork) + (0 + ;; Exit with a non-zero status code if an exception is thrown. + (dynamic-wind + (const #t) + (lambda () + (setenv "HOME" (passwd:dir user-info)) + (setenv "USER" #$user) + (setgid (passwd:gid user-info)) + (setuid (passwd:uid user-info)) + (primitive-exit + (system* #$(file-append package "/bin/gitolite") + "setup" + "-pk" "/var/lib/gitolite/id_rsa.pub"))) + (lambda () + (primitive-exit 1)))) + (pid (waitpid pid))) + + (delete-file "/var/lib/gitolite/id_rsa.pub"))))))) + +(define (gitolite-activation config) + (if (gitolite-configuration-admin-pubkey config) + (gitolite-setup config) + #~(display + "guix: Skipping gitolite setup as the admin-pubkey has not been provided\n"))) + +(define gitolite-service-type + (service-type + (name 'gitolite) + (extensions + (list (service-extension activation-service-type + gitolite-activation) + (service-extension account-service-type + gitolite-accounts) + (service-extension profile-service-type + ;; The Gitolite package in Guix uses + ;; gitolite-shell in the authorized_keys file, so + ;; gitolite-shell needs to be on the PATH for + ;; gitolite to work. + (lambda (config) + (list + (gitolite-configuration-package config)))))) + (default-value (gitolite-configuration)) + (description + "Setup @command{gitolite}, a Git hosting tool providing access over SSH.. +By default, the @code{git} user is used, but this is configurable. +Additionally, Gitolite can integrate with with tools like gitweb or cgit to +provide a web interface to view selected repositories."))) diff --git a/gnu/tests/version-control.scm b/gnu/tests/version-control.scm index 3b935a1b4..e4cd3fc3f 100644 --- a/gnu/tests/version-control.scm +++ b/gnu/tests/version-control.scm @@ -27,14 +27,17 @@ #:use-module (gnu services) #:use-module (gnu services version-control) #:use-module (gnu services cgit) + #:use-module (gnu services ssh) #:use-module (gnu services web) #:use-module (gnu services networking) #:use-module (gnu packages version-control) + #:use-module (gnu packages ssh) #:use-module (guix gexp) #:use-module (guix store) #:use-module (guix modules) #:export (%test-cgit - %test-git-http)) + %test-git-http + %test-gitolite)) (define README-contents "Hello! This is what goes inside the 'README' file.") @@ -300,3 +303,101 @@ HTTP-PORT." (name "git-http") (description "Connect to a running Git HTTP server.") (value (run-git-http-test)))) + + +;;; +;;; Gitolite. +;;; + +(define %gitolite-test-admin-keypair + (computed-file + "gitolite-test-admin-keypair" + (with-imported-modules (source-module-closure + '((guix build utils))) + #~(begin + (use-modules (ice-9 match) (srfi srfi-26) + (guix build utils)) + + (mkdir #$output) + (invoke #$(file-append openssh "/bin/ssh-keygen") + "-f" (string-append #$output "/id_rsa") + "-t" "rsa" + "-q" + "-N" ""))))) + +(define %gitolite-os + (simple-operating-system + (dhcp-client-service) + (service openssh-service-type) + (service gitolite-service-type + (gitolite-configuration + (admin-pubkey + (file-append %gitolite-test-admin-keypair "/id_rsa.pub")))))) + +(define (run-gitolite-test) + (define os + (marionette-operating-system + %gitolite-os + #:imported-modules '((gnu services herd) + (guix combinators)))) + + (define vm + (virtual-machine + (operating-system os) + (port-forwardings `((2222 . 22))))) + + (define test + (with-imported-modules '((gnu build marionette) + (guix build utils)) + #~(begin + (use-modules (srfi srfi-64) + (rnrs io ports) + (gnu build marionette) + (guix build utils)) + + (define marionette + (make-marionette (list #$vm))) + + (mkdir #$output) + (chdir #$output) + + (test-begin "gitolite") + + ;; Wait for sshd to be up and running. + (test-eq "service running" + 'running! + (marionette-eval + '(begin + (use-modules (gnu services herd)) + (start-service 'ssh-daemon) + 'running!) + marionette)) + + (display #$%gitolite-test-admin-keypair) + + (setenv "GIT_SSH_VARIANT" "ssh") + (setenv "GIT_SSH_COMMAND" + (string-join + '(#$(file-append openssh "/bin/ssh") + "-i" #$(file-append %gitolite-test-admin-keypair "/id_rsa") + "-o" "UserKnownHostsFile=/dev/null" + "-o" "StrictHostKeyChecking=no"))) + + ;; Make sure we can clone the repo from the host. + (test-eq "clone" + #t + (invoke #$(file-append git "/bin/git") + "clone" "-v" + "ssh://git@localhost:2222/gitolite-admin" + "/tmp/clone")) + + (test-end) + (exit (= (test-runner-fail-count (test-runner-current)) 0))))) + + (gexp->derivation "gitolite" test)) + +(define %test-gitolite + (system-test + (name "gitolite") + (description "Clone the Gitolite admin repository.") + (value (run-gitolite-test)))) -- 2.17.1