version-control.scm 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2016 Nikita <nikita@n0.is>
  3. ;;; Copyright © 2016 Sou Bunnbu <iyzsong@member.fsf.org>
  4. ;;; Copyright © 2017 Oleg Pykhalov <go.wigust@gmail.com>
  5. ;;; Copyright © 2017 Clément Lassieur <clement@lassieur.org>
  6. ;;; Copyright © 2018 Christopher Baines <mail@cbaines.net>
  7. ;;; Copyright © 2021 Julien Lepiller <julien@lepiller.eu>
  8. ;;;
  9. ;;; This file is part of GNU Guix.
  10. ;;;
  11. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  12. ;;; under the terms of the GNU General Public License as published by
  13. ;;; the Free Software Foundation; either version 3 of the License, or (at
  14. ;;; your option) any later version.
  15. ;;;
  16. ;;; GNU Guix is distributed in the hope that it will be useful, but
  17. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  18. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. ;;; GNU General Public License for more details.
  20. ;;;
  21. ;;; You should have received a copy of the GNU General Public License
  22. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  23. (define-module (gnu services version-control)
  24. #:use-module (gnu services)
  25. #:use-module (gnu services base)
  26. #:use-module (gnu services shepherd)
  27. #:use-module (gnu services web)
  28. #:use-module (gnu system shadow)
  29. #:use-module (gnu packages version-control)
  30. #:use-module (gnu packages admin)
  31. #:use-module (guix records)
  32. #:use-module (guix gexp)
  33. #:use-module (guix store)
  34. #:use-module (srfi srfi-1)
  35. #:use-module (srfi srfi-26)
  36. #:use-module (ice-9 format)
  37. #:use-module (ice-9 match)
  38. #:export (git-daemon-service
  39. git-daemon-service-type
  40. git-daemon-configuration
  41. git-daemon-configuration?
  42. git-http-configuration
  43. git-http-configuration?
  44. git-http-nginx-location-configuration
  45. <gitolite-configuration>
  46. gitolite-configuration
  47. gitolite-configuration-package
  48. gitolite-configuration-user
  49. gitolite-configuration-rc-file
  50. gitolite-configuration-admin-pubkey
  51. <gitolite-rc-file>
  52. gitolite-rc-file
  53. gitolite-rc-file-umask
  54. gitolite-rc-file-unsafe-pattern
  55. gitolite-rc-file-git-config-keys
  56. gitolite-rc-file-roles
  57. gitolite-rc-file-enable
  58. gitolite-service-type
  59. gitile-configuration
  60. gitile-configuration-package
  61. gitile-configuration-host
  62. gitile-configuration-port
  63. gitile-configuration-database
  64. gitile-configuration-repositories
  65. gitile-configuration-git-base-url
  66. gitile-configuration-index-title
  67. gitile-configuration-intro
  68. gitile-configuration-footer
  69. gitile-configuration-nginx
  70. gitile-service-type))
  71. ;;; Commentary:
  72. ;;;
  73. ;;; Version Control related services.
  74. ;;;
  75. ;;; Code:
  76. ;;;
  77. ;;; Git daemon.
  78. ;;;
  79. (define-record-type* <git-daemon-configuration>
  80. git-daemon-configuration
  81. make-git-daemon-configuration
  82. git-daemon-configuration?
  83. (package git-daemon-configuration-package ;package
  84. (default git))
  85. (export-all? git-daemon-configuration-export-all ;boolean
  86. (default #f))
  87. (base-path git-daemon-configuration-base-path ;string | #f
  88. (default "/srv/git"))
  89. (user-path git-daemon-configuration-user-path ;string | #f
  90. (default #f))
  91. (listen git-daemon-configuration-listen ;list of string
  92. (default '()))
  93. (port git-daemon-configuration-port ;number | #f
  94. (default #f))
  95. (whitelist git-daemon-configuration-whitelist ;list of string
  96. (default '()))
  97. (extra-options git-daemon-configuration-extra-options ;list of string
  98. (default '())))
  99. (define git-daemon-shepherd-service
  100. (match-lambda
  101. (($ <git-daemon-configuration>
  102. package export-all? base-path user-path
  103. listen port whitelist extra-options)
  104. (let* ((git (file-append package "/bin/git"))
  105. (command `(,git
  106. "daemon" "--syslog" "--reuseaddr"
  107. ,@(if export-all?
  108. '("--export-all")
  109. '())
  110. ,@(if base-path
  111. `(,(string-append "--base-path=" base-path))
  112. '())
  113. ,@(if user-path
  114. `(,(string-append "--user-path=" user-path))
  115. '())
  116. ,@(map (cut string-append "--listen=" <>) listen)
  117. ,@(if port
  118. `(,(string-append
  119. "--port=" (number->string port)))
  120. '())
  121. ,@extra-options
  122. ,@whitelist)))
  123. (list (shepherd-service
  124. (documentation "Run the git-daemon.")
  125. (requirement '(networking))
  126. (provision '(git-daemon))
  127. (start #~(make-forkexec-constructor '#$command
  128. #:user "git-daemon"
  129. #:group "git-daemon"))
  130. (stop #~(make-kill-destructor))))))))
  131. (define %git-daemon-accounts
  132. ;; User account and group for git-daemon.
  133. (list (user-group
  134. (name "git-daemon")
  135. (system? #t))
  136. (user-account
  137. (name "git-daemon")
  138. (system? #t)
  139. (group "git-daemon")
  140. (comment "Git daemon user")
  141. (home-directory "/var/empty")
  142. (shell (file-append shadow "/sbin/nologin")))))
  143. (define (git-daemon-activation config)
  144. "Return the activation gexp for git-daemon using CONFIG."
  145. (let ((base-path (git-daemon-configuration-base-path config)))
  146. #~(begin
  147. (use-modules (guix build utils))
  148. ;; Create the 'base-path' directory when it's not '#f'.
  149. (and=> #$base-path mkdir-p))))
  150. (define git-daemon-service-type
  151. (service-type
  152. (name 'git-daemon)
  153. (extensions
  154. (list (service-extension shepherd-root-service-type
  155. git-daemon-shepherd-service)
  156. (service-extension account-service-type
  157. (const %git-daemon-accounts))
  158. (service-extension activation-service-type
  159. git-daemon-activation)))
  160. (description
  161. "Expose Git repositories over the insecure @code{git://} TCP-based
  162. protocol.")
  163. (default-value (git-daemon-configuration))))
  164. (define* (git-daemon-service #:key (config (git-daemon-configuration)))
  165. "Return a service that runs @command{git daemon}, a simple TCP server to
  166. expose repositories over the Git protocol for anonymous access.
  167. The optional @var{config} argument should be a
  168. @code{<git-daemon-configuration>} object, by default it allows read-only
  169. access to exported repositories under @file{/srv/git}."
  170. (service git-daemon-service-type config))
  171. ;;;
  172. ;;; HTTP access. Add the result of calling
  173. ;;; git-http-nginx-location-configuration to an nginx-server-configuration's
  174. ;;; "locations" field.
  175. ;;;
  176. (define-record-type* <git-http-configuration>
  177. git-http-configuration
  178. make-git-http-configuration
  179. git-http-configuration?
  180. (package git-http-configuration-package ;package
  181. (default git))
  182. (git-root git-http-configuration-git-root ;string
  183. (default "/srv/git"))
  184. (export-all? git-http-configuration-export-all? ;boolean
  185. (default #f))
  186. (uri-path git-http-configuration-uri-path ;string
  187. (default "/git/"))
  188. (fcgiwrap-socket git-http-configuration-fcgiwrap-socket ;string
  189. (default "127.0.0.1:9000")))
  190. (define* (git-http-nginx-location-configuration #:optional
  191. (config
  192. (git-http-configuration)))
  193. (match config
  194. (($ <git-http-configuration> package git-root export-all?
  195. uri-path fcgiwrap-socket)
  196. (nginx-location-configuration
  197. (uri (string-append "~ /" (string-trim-both uri-path #\/) "(/.*)"))
  198. (body
  199. (list
  200. (list "fastcgi_pass " fcgiwrap-socket ";")
  201. (list "fastcgi_param SCRIPT_FILENAME "
  202. package "/libexec/git-core/git-http-backend"
  203. ";")
  204. "fastcgi_param QUERY_STRING $query_string;"
  205. "fastcgi_param REQUEST_METHOD $request_method;"
  206. "fastcgi_param CONTENT_TYPE $content_type;"
  207. "fastcgi_param CONTENT_LENGTH $content_length;"
  208. (if export-all?
  209. "fastcgi_param GIT_HTTP_EXPORT_ALL \"\";"
  210. "")
  211. (list "fastcgi_param GIT_PROJECT_ROOT " git-root ";")
  212. "fastcgi_param PATH_INFO $1;"))))))
  213. ;;;
  214. ;;; Gitolite
  215. ;;;
  216. (define-record-type* <gitolite-rc-file>
  217. gitolite-rc-file make-gitolite-rc-file
  218. gitolite-rc-file?
  219. (umask gitolite-rc-file-umask
  220. (default #o0077))
  221. (unsafe-pattern gitolite-rc-file-unsafe-pattern
  222. (default #f))
  223. (git-config-keys gitolite-rc-file-git-config-keys
  224. (default ""))
  225. (roles gitolite-rc-file-roles
  226. (default '(("READERS" . 1)
  227. ("WRITERS" . 1))))
  228. (enable gitolite-rc-file-enable
  229. (default '("help"
  230. "desc"
  231. "info"
  232. "perms"
  233. "writable"
  234. "ssh-authkeys"
  235. "git-config"
  236. "daemon"
  237. "gitweb"))))
  238. (define-gexp-compiler (gitolite-rc-file-compiler
  239. (file <gitolite-rc-file>) system target)
  240. (match file
  241. (($ <gitolite-rc-file> umask unsafe-pattern git-config-keys roles enable)
  242. (apply text-file* "gitolite.rc"
  243. `("%RC = (\n"
  244. " UMASK => " ,(format #f "~4,'0o" umask) ",\n"
  245. " GIT_CONFIG_KEYS => '" ,git-config-keys "',\n"
  246. " ROLES => {\n"
  247. ,@(map (match-lambda
  248. ((role . value)
  249. (simple-format #f " ~A => ~A,\n" role value)))
  250. roles)
  251. " },\n"
  252. "\n"
  253. " ENABLE => [\n"
  254. ,@(map (lambda (value)
  255. (simple-format #f " '~A',\n" value))
  256. enable)
  257. " ],\n"
  258. ");\n"
  259. "\n"
  260. ,(if unsafe-pattern
  261. (string-append "$UNSAFE_PATT = qr(" unsafe-pattern ");")
  262. "")
  263. "1;\n")))))
  264. (define-record-type* <gitolite-configuration>
  265. gitolite-configuration make-gitolite-configuration
  266. gitolite-configuration?
  267. (package gitolite-configuration-package
  268. (default gitolite))
  269. (user gitolite-configuration-user
  270. (default "git"))
  271. (group gitolite-configuration-group
  272. (default "git"))
  273. (home-directory gitolite-configuration-home-directory
  274. (default "/var/lib/gitolite"))
  275. (rc-file gitolite-configuration-rc-file
  276. (default (gitolite-rc-file)))
  277. (admin-pubkey gitolite-configuration-admin-pubkey))
  278. (define gitolite-accounts
  279. (match-lambda
  280. (($ <gitolite-configuration> package user group home-directory
  281. rc-file admin-pubkey)
  282. ;; User group and account to run Gitolite.
  283. (list (user-group (name user) (system? #t))
  284. (user-account
  285. (name user)
  286. (group group)
  287. (system? #t)
  288. (comment "Gitolite user")
  289. (home-directory home-directory))))))
  290. (define gitolite-activation
  291. (match-lambda
  292. (($ <gitolite-configuration> package user group home
  293. rc-file admin-pubkey)
  294. #~(begin
  295. (use-modules (ice-9 match)
  296. (guix build utils))
  297. (let* ((user-info (getpwnam #$user))
  298. (admin-pubkey #$admin-pubkey)
  299. (pubkey-file (string-append
  300. #$home "/"
  301. (basename
  302. (strip-store-file-name admin-pubkey))))
  303. (rc-file #$(string-append home "/.gitolite.rc")))
  304. (simple-format #t "guix: gitolite: installing ~A\n" #$rc-file)
  305. (copy-file #$rc-file rc-file)
  306. ;; ensure gitolite's user can read the configuration
  307. (chown rc-file
  308. (passwd:uid user-info)
  309. (passwd:gid user-info))
  310. ;; The key must be writable, so copy it from the store
  311. (copy-file admin-pubkey pubkey-file)
  312. (chmod pubkey-file #o500)
  313. (chown pubkey-file
  314. (passwd:uid user-info)
  315. (passwd:gid user-info))
  316. ;; Set the git configuration, to avoid gitolite trying to use
  317. ;; the hostname command, as the network might not be up yet
  318. (with-output-to-file #$(string-append home "/.gitconfig")
  319. (lambda ()
  320. (display "[user]
  321. name = GNU Guix
  322. email = guix@localhost
  323. ")))
  324. ;; Run Gitolite setup, as this updates the hooks and include the
  325. ;; admin pubkey if specified. The admin pubkey is required for
  326. ;; initial setup, and will replace the previous key if run after
  327. ;; initial setup
  328. (match (primitive-fork)
  329. (0
  330. ;; Exit with a non-zero status code if an exception is thrown.
  331. (dynamic-wind
  332. (const #t)
  333. (lambda ()
  334. (setenv "HOME" (passwd:dir user-info))
  335. (setenv "USER" #$user)
  336. (setgid (passwd:gid user-info))
  337. (setuid (passwd:uid user-info))
  338. (primitive-exit
  339. (system* #$(file-append package "/bin/gitolite")
  340. "setup"
  341. "-m" "gitolite setup by GNU Guix"
  342. "-pk" pubkey-file)))
  343. (lambda ()
  344. (primitive-exit 1))))
  345. (pid (waitpid pid)))
  346. (when (file-exists? pubkey-file)
  347. (delete-file pubkey-file)))))))
  348. (define gitolite-service-type
  349. (service-type
  350. (name 'gitolite)
  351. (extensions
  352. (list (service-extension activation-service-type
  353. gitolite-activation)
  354. (service-extension account-service-type
  355. gitolite-accounts)
  356. (service-extension profile-service-type
  357. ;; The Gitolite package in Guix uses
  358. ;; gitolite-shell in the authorized_keys file, so
  359. ;; gitolite-shell needs to be on the PATH for
  360. ;; gitolite to work.
  361. (lambda (config)
  362. (list
  363. (gitolite-configuration-package config))))))
  364. (description
  365. "Setup @command{gitolite}, a Git hosting tool providing access over SSH..
  366. By default, the @code{git} user is used, but this is configurable.
  367. Additionally, Gitolite can integrate with with tools like gitweb or cgit to
  368. provide a web interface to view selected repositories.")))
  369. ;;;
  370. ;;; Gitile
  371. ;;;
  372. (define-record-type* <gitile-configuration>
  373. gitile-configuration make-gitile-configuration gitile-configuration?
  374. (package gitile-configuration-package
  375. (default gitile))
  376. (host gitile-configuration-host
  377. (default "127.0.0.1"))
  378. (port gitile-configuration-port
  379. (default 8080))
  380. (database gitile-configuration-database
  381. (default "/var/lib/gitile/gitile-db.sql"))
  382. (repositories gitile-configuration-repositories
  383. (default "/var/lib/gitolite/repositories"))
  384. (base-git-url gitile-configuration-base-git-url)
  385. (index-title gitile-configuration-index-title
  386. (default "Index"))
  387. (intro gitile-configuration-intro
  388. (default '()))
  389. (footer gitile-configuration-footer
  390. (default '()))
  391. (nginx gitile-configuration-nginx))
  392. (define (gitile-config-file host port database repositories base-git-url
  393. index-title intro footer)
  394. (define build
  395. #~(write `(config
  396. (port #$port)
  397. (host #$host)
  398. (database #$database)
  399. (repositories #$repositories)
  400. (base-git-url #$base-git-url)
  401. (index-title #$index-title)
  402. (intro #$intro)
  403. (footer #$footer))
  404. (open-output-file #$output)))
  405. (computed-file "gitile.conf" build))
  406. (define gitile-nginx-server-block
  407. (match-lambda
  408. (($ <gitile-configuration> package host port database repositories
  409. base-git-url index-title intro footer nginx)
  410. (list (nginx-server-configuration
  411. (inherit nginx)
  412. (locations
  413. (append
  414. (list
  415. (nginx-location-configuration
  416. (uri "/")
  417. (body
  418. (list
  419. #~(string-append "proxy_pass http://" #$host
  420. ":" (number->string #$port)
  421. "/;")))))
  422. (map
  423. (lambda (loc)
  424. (nginx-location-configuration
  425. (uri loc)
  426. (body
  427. (list
  428. #~(string-append "root " #$package "/share/gitile/assets;")))))
  429. '("/css" "/js" "/images"))
  430. (nginx-server-configuration-locations nginx))))))))
  431. (define gitile-shepherd-service
  432. (match-lambda
  433. (($ <gitile-configuration> package host port database repositories
  434. base-git-url index-title intro footer nginx)
  435. (list (shepherd-service
  436. (provision '(gitile))
  437. (requirement '(loopback))
  438. (documentation "gitile")
  439. (start (let ((gitile (file-append package "/bin/gitile")))
  440. #~(make-forkexec-constructor
  441. `(,#$gitile "-c" #$(gitile-config-file
  442. host port database
  443. repositories
  444. base-git-url index-title
  445. intro footer))
  446. #:user "gitile"
  447. #:group "git")))
  448. (stop #~(make-kill-destructor)))))))
  449. (define %gitile-accounts
  450. (list (user-group
  451. (name "git")
  452. (system? #t))
  453. (user-account
  454. (name "gitile")
  455. (group "git")
  456. (system? #t)
  457. (comment "Gitile user")
  458. (home-directory "/var/empty")
  459. (shell (file-append shadow "/sbin/nologin")))))
  460. (define gitile-service-type
  461. (service-type
  462. (name 'gitile)
  463. (description "Run Gitile, a small Git forge. Expose public repositories
  464. on the web.")
  465. (extensions
  466. (list (service-extension account-service-type
  467. (const %gitile-accounts))
  468. (service-extension shepherd-root-service-type
  469. gitile-shepherd-service)
  470. (service-extension nginx-service-type
  471. gitile-nginx-server-block)))))