From d44b9efda2d387430e63c1d6cd426c88bcde2eda Mon Sep 17 00:00:00 2001 From: Timo Wilken Date: Tue, 12 Sep 2023 19:42:21 +0200 Subject: Factor out proper Git and GnuPG service types --- tw/home.scm | 132 -------------------------------------------------- tw/home/cern.scm | 15 ++++-- tw/home/lap.scm | 22 +++++++-- tw/services/git.scm | 108 +++++++++++++++++++++++++++++++++++++++++ tw/services/gnupg.scm | 94 +++++++++++++++++++++++++++++++++++ 5 files changed, 232 insertions(+), 139 deletions(-) create mode 100644 tw/services/git.scm create mode 100644 tw/services/gnupg.scm diff --git a/tw/home.scm b/tw/home.scm index 026c41d8..4065cf3b 100644 --- a/tw/home.scm +++ b/tw/home.scm @@ -48,60 +48,6 @@ zsh zsh-autosuggestions zsh-history-substring-search zsh-syntax-highlighting zsh-completions)) -(export gnupg-services) ; there doesn't seem to be a `define*-public' macro -(define* (gnupg-services default-key #:key gui-pinentry?) - (list - (simple-service 'gnupg-config home-files-service-type - `(;; GnuPG config files must be in ~/.local/share/gnupg, not ~/.config, - ;; so we can't use `home-xdg-configuration-files-service-type'. - (".local/share/gnupg/dirmngr.conf" - ,(plain-file "dirmngr.conf" "keyserver hkps://keys.openpgp.org")) - (".local/share/gnupg/gpg.conf" - ,(mixed-text-file "gpg.conf" "\ -# This options file can contain any long options to GnuPG. -# See the gpg man page for a list of options. - -default-key " default-key " -default-recipient-self -use-agent -no-greeting # get rid of the copyright notice -# Always encrypt to my key as well, in addition to any recipient. -encrypt-to " default-key " -auto-key-import -auto-key-retrieve -photo-viewer \"" imv "/bin/imv %i\" - -# Because some mailers change lines starting with 'From ' to '>From ' -# it is good to handle such lines in a special way when creating -# cleartext signatures; all other PGP versions do it this way too. -# To enable full OpenPGP compliance you may want to use this option. -#no-escape-from-lines -")) - (".local/share/gnupg/gpg-agent.conf" - ,(mixed-text-file "gpg-agent.conf" "\ -pinentry-program " (if gui-pinentry? - (file-append pinentry-rofi "/bin/pinentry-rofi") - (file-append pinentry-tty "/bin/pinentry-tty")) " -# Keep passphrase cached for longer, so that mcron jobs (e.g. restic, -# vdirsyncer) can access the password store. Vdirsyncer should run every half -# hour to extend the default-cache-ttl. -default-cache-ttl 2100 # 35 min -max-cache-ttl 43200 # 12 h -# Needed if spawning lots of parallel gpg --decrypt processes. https://dev.gnupg.org/T3530 -auto-expand-secmem -")))) - - (simple-service 'gnupg-agent home-shepherd-service-type - (list - (shepherd-service - (documentation "GPG agent; caches key passwords.") - (provision '(gpg-agent)) - (start #~(lambda _ - (invoke #$(file-append gnupg "/bin/gpg-agent") - "--daemon" "--no-detach"))) - (stop #~(lambda _ - (invoke "gpg-connect-agent" "killagent" "/bye")))))))) - (define (wireguardify host) (string-replace-substring host ".twilken.net" ".wg")) @@ -277,7 +223,6 @@ auto-expand-secmem ("ELECTRUMDIR" . "$XDG_DATA_HOME/electrum") ("FG_HOME" . "$XDG_DATA_HOME/fgfs") ("GETIPLAYERUSERPREFS" . "$XDG_DATA_HOME/get_iplayer") - ("GNUPGHOME" . "$XDG_DATA_HOME/gnupg") ("GTK2_RC_FILES" . "$XDG_CONFIG_HOME/gtk-2.0/gtkrc") ("ICEAUTHORITY" . "$XDG_CACHE_HOME/ICEauthority") ("INPUTRC" . "$XDG_CONFIG_HOME/readline/inputrc") @@ -610,80 +555,3 @@ show_border=1 (".local/bin/volume" ,(local-file "home/files/volume" #:recursive? #t)) (".local/share/applications/emacsclient.desktop" ,(local-file "home/files/emacsclient.desktop")))))) - -(define gitconfig-includes - (match-lambda - (() '()) - (((name path _ _) rest ...) - ;; The relative path is relative to the gitconfig file. - (cons (format #f "[includeIf \"gitdir:~a/**\"]\n\tpath = ~aconfig" - (string-trim-right path #\/) name) - (gitconfig-includes rest))) - ((id rest ...) - (cons (format #f "# warning: ignored malformed identity: ~s" id) - (gitconfig-includes rest))))) - -(define gitconfig-otherfiles - (match-lambda - (() '()) - (((name _ email signing-key) rest ...) - `((,(string-append "git/" name "config") - ,(plain-file (string-append "git" name "config") - (string-append - "[user]\n" - (if email (string-append "\temail = " email "\n") "") - (if signing-key (string-append "\tsigningkey = " signing-key "\n") "")))) - ,@(gitconfig-otherfiles rest))) - ((id rest ...) - (format (current-error-port) "warning: ignored malformed gitconfig identity: ~s~%" id) - (gitconfig-otherfiles rest)))) - -(export gitconfig) -(define* (gitconfig default-email default-signing-key #:rest identities) - (simple-service 'gitconfig home-xdg-configuration-files-service-type - `(,@(gitconfig-otherfiles identities) - ("git/config" ,(mixed-text-file "gitconfig" "\ -# This is Git's per-user configuration file. -[user] - name = Timo Wilken - email = " default-email " - signingkey = " default-signing-key - (string-join (gitconfig-includes identities) "\n" 'prefix) " -[commit] - gpgsign = true -[url \"ssh://git@gitlab.cern.ch:7999/\"] - insteadOf = https://gitlab.cern.ch/ -[url \"ssh://git@ssh.github.com/\"] - insteadOf = gh: -[gui] - fontui = -family \\\"Fira Sans\\\" -size 10 -weight normal -slant roman -underline 0 -overstrike 0 - fontdiff = -family \\\"Hermit\\\" -size 10 -weight normal -slant roman -underline 0 -overstrike 0 - tabsize = 4 -[merge] - summary = true - conflictstyle = diff3 -[color] - ui = auto -[pull] - rebase = false - ff = only -[alias] - glog = log --decorate --graph --oneline - plog = log --decorate --graph --oneline --pretty=tformat:\\\"%C(yellow)%h %Cgreen%as %Cblue%<(10,trunc)%an%Cred%d%Creset %s\\\" -[init] - defaultBranch = master -[advice] - detachedHead = false - addEmptyPathspec = false -# https://sw.kovidgoyal.net/kitty/kittens/diff/ -[diff] - tool = kitty - guitool = kitty.gui -[difftool] - prompt = false - trustExitCode = true -[difftool \"kitty\"] - cmd = " kitty "/bin/kitty +kitten diff $LOCAL $REMOTE -[difftool \"kitty.gui\"] - cmd = " kitty "/bin/kitty " kitty "/bin/kitty +kitten diff $LOCAL $REMOTE -"))))) diff --git a/tw/home/cern.scm b/tw/home/cern.scm index f0dc7214..8c6ed7d5 100644 --- a/tw/home/cern.scm +++ b/tw/home/cern.scm @@ -30,6 +30,8 @@ #:use-module (tw packages catppuccin) #:use-module (tw packages xorg) #:use-module (tw gexp) + #:use-module (tw services git) + #:use-module (tw services gnupg) #:use-module (tw theme)) (use-package-modules calendar dav mail web-browsers xdisorg xorg) @@ -128,11 +130,18 @@ "/X11/ridge-view.jpg"))))))) ;; On my work machine, Git must always use my work PGP key. - (gitconfig "timo.wilken@cern.ch" "C2249BBE5E8761C943A0CFA1B7B3914BF63ACD7C") + (service home-git-service-type + (home-git-configuration + (default-email "timo.wilken@cern.ch") + (default-signing-key "C2249BBE5E8761C943A0CFA1B7B3914BF63ACD7C"))) (openssh-service #f) - (append %common-services %interactive-services pim-services - (gnupg-services "C2249BBE5E8761C943A0CFA1B7B3914BF63ACD7C" #:gui-pinentry? #t)))))) + (service home-gnupg-service-type + (home-gnupg-configuration + (default-key "C2249BBE5E8761C943A0CFA1B7B3914BF63ACD7C") + (gui-pinentry? #t))) + + (append %common-services %interactive-services pim-services))))) %cern-home diff --git a/tw/home/lap.scm b/tw/home/lap.scm index c166c75e..ae5fb629 100644 --- a/tw/home/lap.scm +++ b/tw/home/lap.scm @@ -38,6 +38,8 @@ #:use-module ((nongnu packages steam-client) #:select (steam)) #:use-module (tw home) + #:use-module (tw services git) + #:use-module (tw services gnupg) #:use-module (tw packages scanner) #:use-module (tw services restic) #:use-module (tw theme)) @@ -202,12 +204,24 @@ ;; On my private machine, I want to use my private PGP key normally, and ;; my work key only for work repositories. - (gitconfig "git@twilken.net" "53EC3C06856883DD92355BC22FC78504681F69B0" - '("cern" "~/src/alice" "timo.wilken@cern.ch" "C2249BBE5E8761C943A0CFA1B7B3914BF63ACD7C")) + (service home-git-service-type + (home-git-configuration + (default-email "git@twilken.net") + (default-signing-key "53EC3C06856883DD92355BC22FC78504681F69B0") + (identities + (list (home-git-identity + (name "cern") + (root-directory "~/src/alice") + (email "timo.wilken@cern.ch") + (signing-key "C2249BBE5E8761C943A0CFA1B7B3914BF63ACD7C")))))) (openssh-service #t) - (append pim-services %interactive-services %common-services - (gnupg-services "53EC3C06856883DD92355BC22FC78504681F69B0" #:gui-pinentry? #t)))))) + (service home-gnupg-service-type + (home-gnupg-configuration + (default-key "53EC3C06856883DD92355BC22FC78504681F69B0") + (gui-pinentry? #t))) + + (append pim-services %interactive-services %common-services))))) %lap-home diff --git a/tw/services/git.scm b/tw/services/git.scm new file mode 100644 index 00000000..69dae1e8 --- /dev/null +++ b/tw/services/git.scm @@ -0,0 +1,108 @@ +(define-module (tw services git) + #:use-module (gnu) + #:use-module (gnu home services) + #:use-module (gnu packages terminals) + #:use-module (gnu packages version-control) + #:use-module (gnu services configuration) + #:use-module (guix gexp) + #:use-module (guix packages) + #:use-module ((guix records) #:select (match-record)) + #:use-module (srfi srfi-1) + #:export (home-git-identity + home-git-configuration + home-git-service-type)) + +(define-configuration/no-serialization home-git-identity + (name string "Short slug identifying this identity. Only used to name config files.") + (root-directory string "Directory under which this identity should apply.") + (email string "Author commits as this email for this identity.") + (signing-key string "Sign commits using this key for this identity.")) + +(define (list-of-identities? thing) + (and (list? thing) (every home-git-identity? thing))) + +(define-configuration/no-serialization home-git-configuration + (default-email string "Author git commits as this email.") + (default-signing-key string "Sign git commits using this email.") + (identities (list-of-identities '()) "Override default configuration values +under specific directories, e.g. to use a work identity in a directory +containing work projects.")) + +(define (gitconfig-includes identities) + (if (null? identities) '() + (cons (match-record (car identities) + (name root-directory) + ;; A relative path is relative to the gitconfig file. + (format #f "[includeIf \"gitdir:~a/**\"]\n\tpath = ~aconfig" + (string-trim-right root-directory #\/) name)) + (gitconfig-includes (cdr identities))))) + +(define (gitconfig-otherfiles identities) + (if (null? identities) '() + (cons (match-record (car identities) + (name email signing-key) + (list + (string-append "git/" name "config") + (plain-file (string-append "git" name "config") + (string-append + "[user]\n" + (if email (string-append "\temail = " email "\n") "") + (if signing-key (string-append "\tsigningkey = " signing-key "\n") ""))))) + (gitconfig-otherfiles (cdr identities))))) + +(define (gitconfig config) + (match-record config + (default-email default-signing-key identities) + `(,@(gitconfig-otherfiles identities) + ("git/config" ,(mixed-text-file "gitconfig" "\ +# This is Git's per-user configuration file. +[user] + name = Timo Wilken + email = " default-email " + signingkey = " default-signing-key + (string-join (gitconfig-includes identities) "\n" 'prefix) " +[commit] + gpgsign = true +[url \"ssh://git@gitlab.cern.ch:7999/\"] + insteadOf = https://gitlab.cern.ch/ +[url \"ssh://git@ssh.github.com/\"] + insteadOf = gh: +[gui] + fontui = -family \\\"Fira Sans\\\" -size 10 -weight normal -slant roman -underline 0 -overstrike 0 + fontdiff = -family \\\"Hermit\\\" -size 10 -weight normal -slant roman -underline 0 -overstrike 0 + tabsize = 4 +[merge] + summary = true + conflictstyle = diff3 +[color] + ui = auto +[pull] + rebase = false + ff = only +[alias] + glog = log --decorate --graph --oneline + plog = log --decorate --graph --oneline --pretty=tformat:\\\"%C(yellow)%h %Cgreen%as %Cblue%<(10,trunc)%an%Cred%d%Creset %s\\\" +[init] + defaultBranch = master +[advice] + detachedHead = false + addEmptyPathspec = false +# https://sw.kovidgoyal.net/kitty/kittens/diff/ +[diff] + tool = kitty + guitool = kitty.gui +[difftool] + prompt = false + trustExitCode = true +[difftool \"kitty\"] + cmd = " kitty "/bin/kitty +kitten diff $LOCAL $REMOTE +[difftool \"kitty.gui\"] + cmd = " kitty "/bin/kitty " kitty "/bin/kitty +kitten diff $LOCAL $REMOTE +"))))) + +(define home-git-service-type + (service-type + (name 'git) + (extensions + (list (service-extension home-xdg-configuration-files-service-type gitconfig))) + (description "Configure Git."))) diff --git a/tw/services/gnupg.scm b/tw/services/gnupg.scm new file mode 100644 index 00000000..9b358ea4 --- /dev/null +++ b/tw/services/gnupg.scm @@ -0,0 +1,94 @@ +(define-module (tw services gnupg) + #:use-module (gnu) + #:use-module (gnu home services) + #:use-module (gnu home services shepherd) + #:use-module (gnu packages gnupg) + #:use-module ((gnu packages image-viewers) + #:select (imv)) + #:use-module (gnu services configuration) + #:use-module (guix gexp) + #:use-module (guix packages) + #:use-module ((guix records) #:select (match-record)) + #:export (home-gnupg-configuration + home-gnupg-service-type)) + +(define-configuration/no-serialization home-gnupg-configuration + (default-key string "The user's own key. Always encrypt to this key, and +use it by default.") + (gui-pinentry? boolean "Use pinentry-rofi if true, else pinentry-tty.") + (keyserver (string "hkps://keys.openpgp.org") "The default keyserver to use.") + (gnupg (package gnupg) "The GnuPG package to use.") + (image-viewer (file-like (file-append imv "/bin/imv")) "A gexp returning a +string, specifying the command to call in order to view images.") + (gnupghome (string "$XDG_DATA_HOME/gnupg") "The value of $GNUPGHOME in the +environment.")) + +(define (gnupg-xdg config) + `(("GNUPGHOME" . ,(home-gnupg-configuration-gnupghome config)))) + +(define (gnupg-files config) + (match-record config + (default-key gui-pinentry? keyserver image-viewer) + `(;; GnuPG config files must be in ~/.local/share/gnupg, not ~/.config, + ;; so we can't use `home-xdg-configuration-files-service-type'. + (".local/share/gnupg/dirmngr.conf" + ,(mixed-text-file "dirmngr.conf" + "keyserver " keyserver "\n")) + (".local/share/gnupg/gpg.conf" + ,(mixed-text-file "gpg.conf" "\ +# This options file can contain any long options to GnuPG. +# See the gpg man page for a list of options. +# Comments can only be at the start of a line, not after options. + +default-key " default-key " +default-recipient-self +use-agent +# Get rid of the copyright notice. +no-greeting +# Always encrypt to my key as well, in addition to any recipient. +encrypt-to " default-key " +auto-key-import +auto-key-retrieve +photo-viewer \"" image-viewer " %i\" + +# Because some mailers change lines starting with 'From ' to '>From ' +# it is good to handle such lines in a special way when creating +# cleartext signatures; all other PGP versions do it this way too. +# To enable full OpenPGP compliance you may want to use this option. +#no-escape-from-lines +")) + (".local/share/gnupg/gpg-agent.conf" + ,(mixed-text-file "gpg-agent.conf" "\ +pinentry-program " (if gui-pinentry? + (file-append pinentry-rofi "/bin/pinentry-rofi") + (file-append pinentry-tty "/bin/pinentry-tty")) " +# Keep passphrase cached for longer, so that mcron jobs (e.g. restic, +# vdirsyncer) can access the password store. Vdirsyncer should run every half +# hour to extend the default-cache-ttl. +default-cache-ttl 2100 # 35 min +max-cache-ttl 43200 # 12 h +# Needed if spawning lots of parallel gpg --decrypt processes. https://dev.gnupg.org/T3530 +auto-expand-secmem +"))))) + +(define (gnupg-shepherd config) + (match-record config (gnupg) + (list (shepherd-service + (documentation "GPG agent; caches key passwords.") + (provision '(gpg-agent)) + (start #~(lambda _ + (invoke #$(file-append gnupg "/bin/gpg-agent") + "--daemon" "--no-detach"))) + (stop #~(lambda _ + (invoke #$(file-append gnupg "/bin/gpg-connect-agent") + "killagent" "/bye"))))))) + +(define home-gnupg-service-type + (service-type + (name 'gnupg) + (extensions + (list (service-extension home-shepherd-service-type gnupg-shepherd) + (service-extension home-files-service-type gnupg-files) + (service-extension home-environment-variables-service-type gnupg-xdg))) + (description + "Install GnuPG configuration files and run the agent."))) -- cgit v1.2.3