From a29ac771b3b8aabd00fe325812a9b6431d814634 Mon Sep 17 00:00:00 2001 From: TakeV Date: Thu, 20 Jul 2023 17:27:49 -0700 Subject: [PATCH] Add gitea. We will be bootstrapping forgejo from this --- guix-punk/packages/version-control.scm | 62 ++++++ guix-punk/services/version-control.scm | 289 +++++++++++++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 guix-punk/packages/version-control.scm create mode 100644 guix-punk/services/version-control.scm diff --git a/guix-punk/packages/version-control.scm b/guix-punk/packages/version-control.scm new file mode 100644 index 0000000..8aa65ca --- /dev/null +++ b/guix-punk/packages/version-control.scm @@ -0,0 +1,62 @@ +;;; SPDX-License-Identifier: GPL-3.0-or-later +;;; This is a patch from the nonguix repo: +;;; https://gitlab.com/nonguix/nonguix/-/merge_requests/279?commit_id=3ac4f3a0153aa6a6f196a3dfde2111faee06278f#98db49bbf79c0f0288aa1baf1d2d0038c694466e +;;; TODO Remove patch when merged into nonguix + +(define-module (guix-punk packages version-control) + #:use-module (gnu packages base) + #:use-module (gnu packages golang) + #:use-module (gnu packages version-control) + #:use-module (guix build-system gnu) + #:use-module (guix download) + #:use-module (guix gexp) + #:use-module ((guix licenses) #:prefix license:) + #:use-module (guix packages) + #:use-module (ice-9 match) + #:use-module (nonguix build-system binary)) + +(define-public gitea + (package + (name "gitea") + (version "1.19.1") + (source (origin + (method url-fetch) + (uri (string-append + "https://github.com/go-gitea/gitea/releases/download/v" version "/gitea-src-" + version ".tar.gz")) + (sha256 + (base32 + "1yamzgckfb9127s8sijjly7w5s05c19chr1jl8ni8hkc0p3b2rwb")))) + (build-system gnu-build-system) + (arguments + (list #:phases + #~(modify-phases %standard-phases + (delete 'configure) + (replace 'build (lambda _ + (setenv "HOME" (getcwd)) + (setenv "TAGS" "bindata sqlite sqlite_unlock_notify") + (setenv "LDFLAGS" + (let* ((format-flag (lambda (var val) + (string-append + "-X \"code.gitea.io/gitea/modules/setting." var "=" val "\""))) + (format-flags (lambda (lst) (string-join (map (lambda (x) (apply format-flag x)) + lst))))) + (format-flags + '(("CustomPath" "/var/lib/gitea/custom") + ("AppWorkPath" "/var/lib/gitea"))))) + (invoke "make" "build"))) + (delete 'check) + (replace 'install + (lambda _ + (install-file "gitea" (string-append #$output "/bin/")))) + (add-after 'install 'wrap-programs + (lambda* _ + (wrap-program (string-append #$output "/bin/gitea") + `("PATH" prefix (,(string-append #$(this-package-input "git") "/bin"))))))))) + (native-inputs (list go-1.19)) + (inputs (list git)) + (home-page "https://gitea.io") + (synopsis "A painless self-hosted Git service") + (description "Gitea is a community managed lightweight code hosting +solution written in Go. It is published under the MIT license") + (license license:expat))) diff --git a/guix-punk/services/version-control.scm b/guix-punk/services/version-control.scm new file mode 100644 index 0000000..7ac29d1 --- /dev/null +++ b/guix-punk/services/version-control.scm @@ -0,0 +1,289 @@ +;;; SPDX-License-Identifier: GPL-3.0-or-later +;;; This is a patch from the nonguix repo: +;;; https://gitlab.com/nonguix/nonguix/-/merge_requests/279?commit_id=3ac4f3a0153aa6a6f196a3dfde2111faee06278f#98db49bbf79c0f0288aa1baf1d2d0038c694466e +;;; TODO Remove patch when merged into nonguix + + +;;; Copyright © 2023 Saku Laesvuori + +(define-module (nongnu services version-control) + #:use-module ((rnrs) :version (6)) + #:use-module (gnu packages base) + #:use-module (gnu packages bash) + #:use-module (gnu services base) + #:use-module (gnu services certbot) + #:use-module (gnu services configuration) + #:use-module (gnu services databases) + #:use-module (gnu services shepherd) + #:use-module (gnu services web) + #:use-module (gnu services) + #:use-module (gnu system shadow) + #:use-module (guix gexp) + #:use-module (guix packages) + #:use-module (guix records) + #:use-module (ice-9 match) + #:use-module (ice-9 string-fun) + #:use-module (nongnu packages version-control) + #:use-module (srfi srfi-1) + #:export + (gitea-configuration + gitea-service-type)) + +(define (gitea-fields? x) + (match x + (((section . ((name . value) ...)) ...) + (and (all symbol? section) + (all symbol? name))) + (_ + #f))) + +(define-maybe string) + +(define-configuration/no-serialization gitea-configuration + (app-name + (string "Gitea: Git with a cup of tea") + "Instance name") + (domain + (string "localhost") + "Domain name of the server") + (root-url + (maybe-string) + "Public root url, default http(s)://DOMAIN/") + (extra-gitea-fields + (gitea-fields '()) + "Extra gitea configuration") + (https? + (boolean #t) + "Set up HTTPS with certbot") + (nginx? + (boolean #t) + "Set up NGINX as a reverse proxy") + (postgresql? + (boolean #t) + "Set up a PostgreSQL database") + (gitea + (package gitea) + "The Gitea package to use") + (work-dir + (string "/var/lib/gitea") + "Gitea working directory") + (run-dir + (string "/var/run/gitea") + "Gitea runtime directory") + (internal-token + (maybe-string) + "Path to gitea's internal token")) + +(define (gitea-internal-token config) + (match-record config + (internal-token work-dir) + (if (maybe-value-set? internal-token) + internal-token + (string-append work-dir "/internal-token")))) + +(define (gitea-configuration->fields config) + (match-record config + (app-name domain root-url extra-gitea-fields nginx? https? postgresql? run-dir) + (append + `((DEFAULT . ((app-name . ,app-name))) + (security . ((internal-token-uri . ,(string-append "file:" (gitea-internal-token config))) + (install-lock . #t))) + ,@(if postgresql? + '((database . ((db-type . "postgres") + (host . "/var/run/postgresql/") + (user . "gitea") + (name . "gitea")))) + '()) + (server . ((domain . ,domain) + ,@(if root-url `((root-url . ,root-url)) '()) + ,@(if nginx? + `((protocol . "http+unix") + (http-addr . ,(string-append run-dir "/gitea.socket"))) + (if https? + `((protocol . "https") + (cert-file . ,(string-append "/etc/letsencrypt/live/" domain "/fullchain.pem")) + (key-file . ,(string-append "/etc/letsencrypt/live/" domain "/privkey.pem"))) + '()))))) + extra-gitea-fields))) + +(define (gitea-shepherd-service config) + (match-record config + (work-dir run-dir postgresql? gitea) + (let ((config-file (gitea-serialize-config config))) + (list (shepherd-service + (documentation "Run the gitea server") + (requirement `(networking ,@(if postgresql? '(postgres) '()))) + (provision '(gitea)) + (start #~(make-forkexec-constructor + (list + #$(file-append gitea "/bin/gitea") + "web" + "--config" #$config-file + "--work-path" #$work-dir + "--custom-path" (string-append #$work-dir "/custom") + "--pid" (string-append #$run-dir "/pid")) + #:user "gitea" + #:group "gitea" + #:directory #$work-dir + ; TODO can these be set automatically + #:environment-variables (cons* + "USER=gitea" + (string-append "HOME=" #$work-dir) + (default-environment-variables)))) + (stop #~(make-kill-destructor)) + (actions (list (shepherd-configuration-action config-file)))))))) + +(define (gitea-certbot config) + (match-record config + (https? domain nginx?) + (if (not https?) '() + (list (certificate-configuration + (domains (list domain)) + (deploy-hook (if nginx? %nginx-cert-deploy-hook %gitea-cert-deploy-hook))))))) + +(define (gitea-postgresql-roles config) + (match-record config + (postgresql?) + (if postgresql? + (list (postgresql-role + (name "gitea") + (create-database? #t))) + '()))) + +(define (gitea-accounts config) + (match-record config + (work-dir) + (list (user-group + (name "gitea") + (system? #t)) + (user-account + (name "gitea") + (system? #t) + (group "gitea") + (comment "Gitea server user") + (home-directory work-dir) + (shell (file-append bash-minimal "/bin/bash")))))) + +(define (gitea-activation config) + (match-record config + (work-dir run-dir) + #~(begin + (use-modules (guix build utils) + (ice-9 string-fun)) + (let* ((user (getpw "gitea")) + (user-id (passwd:uid user)) + (group-id (passwd:gid user)) + (internal-token #$(gitea-internal-token config)) + (shell-escape (lambda (str) + (string-append "'" + (string-replace-substring str "'" "'\"'\"'") + "'"))) + (generate-internal-token + (lambda _ + (if (not (file-exists? internal-token)) + (system* + (string-append #$bash-minimal "/bin/bash") "-c" + (string-append + "umask 277 ; " + ; /dev/random blocks if there isn't enough entropy which + ; might be useful as this might be ran right after booting + #$coreutils "/bin/head -c 30 /dev/urandom | " + #$coreutils "/bin/base64 > " (shell-escape internal-token))))))) + (mkdir-p #$work-dir) + (mkdir-p #$run-dir) + (unless (file-exists? internal-token) (generate-internal-token)) + (chown #$work-dir user-id group-id) + (chown #$run-dir user-id group-id) + (chown internal-token user-id group-id))))) + +(define (gitea-nginx config) + (match-record config + (https? domain nginx? run-dir) + (if (not nginx?) '() + (list (nginx-server-configuration + (listen (if https? '("443 ssl") '("80"))) + (server-name (list domain)) + (ssl-certificate (if https? (string-append "/etc/letsencrypt/live/" domain "/fullchain.pem") #f)) + (ssl-certificate-key (if https? (string-append "/etc/letsencrypt/live/" domain "/privkey.pem") #f)) + (locations (list + (nginx-location-configuration + (uri "/") + (body `(,(string-append "proxy_pass http://unix:" run-dir "/gitea.socket;") + "proxy_set_header Host $host;" + "proxy_set_header X-Real_IP $remote_addr;" + "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;" + "proxy_set_header X-Forwarded-Proto $scheme;")))))))))) + +(define %nginx-cert-deploy-hook + (program-file + "nginx-cert-deploy-hook" + #~(let ((pid (call-with-input-file "/var/run/nginx/pid" read))) + (kill pid SIGHUP)))) + +(define %gitea-cert-deploy-hook + (program-file + "gitea-cert-deploy-hook" + (with-imported-modules '((guix build utils)) + #~(begin (use-modules (guix build utils)) + (invoke "herd" "restart" "gitea"))))) + +(define (gitea-serialize-name name) + (let ((str (symbol->string name))) + (string-replace-substring + (if (string-suffix? "?" str) + (string-drop-right str 1) + str) + "-" + "_"))) + +(define (gitea-serialize-field-name field-name) + (string-upcase (gitea-serialize-name field-name))) + +(define (gitea-escape-string str) + (string-append "`" (string-replace-substring str "`" "\\`") "`")) + ; this is not safe escaping as an input of \`evil\` gets transformed into `\\`evil\\``, escaping the quotes. + ; However, the input is from a semi-trusted source, so it's not all that bad. + ; At worst a user can mess up their config without knowing how and why + +(define (gitea-serialize-config config) + (gitea-serialize-fields (gitea-configuration->fields config))) + +(define (gitea-serialize-fields fields) + (plain-file "app.ini" (string-concatenate (map gitea-serialize-sections fields)))) + +(define (gitea-serialize-sections sections) + (match sections + ((section . (fields ...)) + (string-append "[" (gitea-serialize-name section) "]\n" + (string-concatenate (map gitea-serialize-field fields)))))) + +(define (gitea-serialize-field field) + (match field + ((name . value) + (string-append (gitea-serialize-field-name name) " = " (gitea-serialize-value value) "\n")))) + +(define (gitea-serialize-value value) + (cond ((number? value) (number->string value)) + ((boolean? value) (if value "true" "false")) + ((string? value) (gitea-escape-string value)) + ((list? value) (string-join (map gitea-serialize-value value) ",")) + (else (error (format #f "Invalid type for gitea-serialize-value: ~a" value))))) + +(define (all pred struct) + (match struct + ('() #t) + ((a . b) (and (all pred a) (all pred b))) + (leaf (pred leaf)))) + +(define gitea-service-type + (service-type + (name 'gitea) + (extensions + (list (service-extension account-service-type gitea-accounts) + (service-extension activation-service-type gitea-activation) + (service-extension shepherd-root-service-type gitea-shepherd-service) + (service-extension nginx-service-type gitea-nginx) + (service-extension certbot-service-type gitea-certbot) + (service-extension postgresql-role-service-type gitea-postgresql-roles))) + (description "Run the gitea server") + (default-value (gitea-configuration))))