From a7dcb8ed92a6f0ccfbced8422f916fd6d9b1c781 Mon Sep 17 00:00:00 2001 From: EuAndreh Date: Sat, 17 Aug 2024 18:44:59 -0300 Subject: Offload common server code to dedicated packages --- src/config/conf.env | 16 -- src/config/conf.env.in | 16 -- src/config/gitconfig | 11 -- src/config/init.scm | 6 - src/config/known_hosts.txt | 5 - src/config/profile.sh | 5 - src/config/rc.sh | 80 -------- src/config/ssh.conf | 6 - src/guix/channels.scm | 4 +- src/guix/packages.scm | 55 +++--- src/guix/services.scm | 187 ------------------- src/guix/system.scm | 448 ++++++--------------------------------------- src/scripts/backup.sh | 149 --------------- src/scripts/check.sh | 92 ---------- src/scripts/cicd.sh | 168 ----------------- src/scripts/cronjob.sh | 169 ----------------- src/scripts/deploy.sh | 72 -------- src/scripts/gc.sh | 146 --------------- src/scripts/reconfigure.sh | 146 --------------- src/scripts/report.sh | 260 -------------------------- 20 files changed, 79 insertions(+), 1962 deletions(-) delete mode 100644 src/config/conf.env delete mode 100644 src/config/conf.env.in delete mode 100644 src/config/gitconfig delete mode 100644 src/config/init.scm delete mode 100644 src/config/known_hosts.txt delete mode 100644 src/config/profile.sh delete mode 100644 src/config/rc.sh delete mode 100644 src/config/ssh.conf delete mode 100644 src/guix/services.scm delete mode 100755 src/scripts/backup.sh delete mode 100755 src/scripts/check.sh delete mode 100755 src/scripts/cicd.sh delete mode 100755 src/scripts/cronjob.sh delete mode 100755 src/scripts/deploy.sh delete mode 100755 src/scripts/gc.sh delete mode 100755 src/scripts/reconfigure.sh delete mode 100755 src/scripts/report.sh (limited to 'src') diff --git a/src/config/conf.env b/src/config/conf.env deleted file mode 100644 index b60173e..0000000 --- a/src/config/conf.env +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -NAME='papo.im' -TLD='papo.im' -URL='papo.im' -OFFSITE_SSH='00000@aa0000.rsync.net' -OUT_SUFFIX='' -PRIV_SUFFIX='' -CI_SUFFIX="ci" - -HTML_OUTDIR_TOP="/srv/www/$OUT_SUFFIX" -HTML_OUTDIR_PRIV="$HTML_OUTDIR_TOP$PRIV_SUFFIX" -HTML_OUTDIR_CI="$HTML_OUTDIR_TOP/$CI_SUFFIX" -HOMEPAGE="https://$TLD/$OUT_SUFFIX/" -CGIT_URL="https://$TLD/git/$NAME/commit/?id=" -REPO_NAME="$NAME.git" diff --git a/src/config/conf.env.in b/src/config/conf.env.in deleted file mode 100644 index e54daef..0000000 --- a/src/config/conf.env.in +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -NAME='@NAME@' -TLD='@TLD@' -URL='@URL@' -OFFSITE_SSH='@OFFSITE_SSH@' -OUT_SUFFIX='' -PRIV_SUFFIX='' -CI_SUFFIX="ci" - -HTML_OUTDIR_TOP="/srv/www/$OUT_SUFFIX" -HTML_OUTDIR_PRIV="$HTML_OUTDIR_TOP$PRIV_SUFFIX" -HTML_OUTDIR_CI="$HTML_OUTDIR_TOP/$CI_SUFFIX" -HOMEPAGE="https://$TLD/$OUT_SUFFIX/" -CGIT_URL="https://$TLD/git/$NAME/commit/?id=" -REPO_NAME="$NAME.git" diff --git a/src/config/gitconfig b/src/config/gitconfig deleted file mode 100644 index f1f1eb3..0000000 --- a/src/config/gitconfig +++ /dev/null @@ -1,11 +0,0 @@ -[init] - defaultBranch = main -[user] - email = ci@$TLD - name = "Git CI" -[advice] - detachedHead = false -[receive] - advertisePushOptions = true -[uploadpack] - allowAnySHA1InWant = true diff --git a/src/config/init.scm b/src/config/init.scm deleted file mode 100644 index 9e962e8..0000000 --- a/src/config/init.scm +++ /dev/null @@ -1,6 +0,0 @@ -(use-modules - (ice-9 colorized) - (ice-9 readline)) - -(activate-colorized) -(activate-readline) diff --git a/src/config/known_hosts.txt b/src/config/known_hosts.txt deleted file mode 100644 index 74ba219..0000000 --- a/src/config/known_hosts.txt +++ /dev/null @@ -1,5 +0,0 @@ -# rsync.net public keys -# Verified in 2023-03-08 at: -# https://www.rsync.net/resources/fingerprints.txt - -hk-s020.rsync.net ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILcPl9x9JfRFwsn09NnDw/xBZbAN80ZQck+h6AqlVqPH diff --git a/src/config/profile.sh b/src/config/profile.sh deleted file mode 100644 index 1dca8b2..0000000 --- a/src/config/profile.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -# shellcheck source=/dev/null -. /etc/rc -ln -fs .profile .bashrc diff --git a/src/config/rc.sh b/src/config/rc.sh deleted file mode 100644 index b44d3d1..0000000 --- a/src/config/rc.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/sh - -# shellcheck source=/dev/null -. /etc/profile -. /etc/conf.env - -export XDG_PREFIX=~/.usr -export XDG_CACHE_HOME="$XDG_PREFIX"/var/cache -export XDG_CONFIG_HOME="$XDG_PREFIX"/etc -export XDG_DATA_HOME="$XDG_PREFIX"/share -export XDG_STATE_HOME="$XDG_PREFIX"/state -export XDG_LOG_HOME="$XDG_PREFIX"/var/log - - -HOME_PARENT="$(dirname "$HOME")" -if [ "$HOME_PARENT" = '/home' ] || [ "$HOME_PARENT" = '/' ]; then - mkdir -p \ - "$XDG_CONFIG_HOME" \ - "$XDG_CACHE_HOME" \ - "$XDG_DATA_HOME" \ - "$XDG_LOG_HOME" \ - "$XDG_STATE_HOME"/ssh/conn -fi - - -GUIX_PROFILE="$XDG_CONFIG_HOME"/guix/current -if [ -r "$GUIX_PROFILE"/etc/profile ]; then - # shellcheck source=/dev/null - . "$GUIX_PROFILE"/etc/profile -fi - -export ENV=~/.profile -export HISTSIZE=-1 -export HISTCONTROL=ignorespace:ignoredups -export EDITOR=vi -export VISUAL="$EDITOR" -export PAGER='less -R' - -export EXINIT=' - " set number - " set autoindent - set ruler - set showmode - set showmatch -' - -export HISTFILE="$XDG_STATE_HOME"/bash-history -export LESSHISTFILE="$XDG_STATE_HOME"/lesshst -export RLWRAP_HOME="$XDG_CACHE_HOME"/rlwrap -export GUILE_HISTORY="$XDG_STATE_HOME"/guile-history - -HOSTNAME="$(hostname)" -export BORG_REPO="$OFFSITE_SSH:borg/$HOSTNAME" -export BORG_REMOTE_PATH='borg1' -export BORG_PASSCOMMAND='cat /opt/secrets/borg-passphrase.txt' - -export GIT_CONFIG_GLOBAL=/etc/gitconfig - -unalias -a -alias l='ls -lahF --color' -alias grep='grep --color=auto' -alias diff='diff --color=auto' -alias watch='watch --color ' -alias man='MANWIDTH=$((COLUMNS > 80 ? 80 : COLUMNS)) man' -alias less='less -R' -alias tree='tree -aC' -alias mv='mv -i' -alias e='vi' - -alias sqlite='rlwrap sqlite3' -alias guile='guile -l /etc/init.scm' - -error_marker() { - STATUS=$? - if [ "$STATUS" != 0 ]; then - printf ' (!! %s !!) ' "$STATUS" - fi -} -export PS1='`error_marker`\T \w/ -\u@\H\$ ' diff --git a/src/config/ssh.conf b/src/config/ssh.conf deleted file mode 100644 index ca41df0..0000000 --- a/src/config/ssh.conf +++ /dev/null @@ -1,6 +0,0 @@ -Host * - ServerAliveInterval 30 - ServerAliveCountMax 20 - ControlMaster auto - ControlPath ${XDG_STATE_HOME}/ssh/conn/%r@%h:%p - ControlPersist 1h diff --git a/src/guix/channels.scm b/src/guix/channels.scm index 16a9c7d..697eb4f 100644 --- a/src/guix/channels.scm +++ b/src/guix/channels.scm @@ -1,8 +1,8 @@ (append (list (channel - (name 'org-euandre) - (url "git://euandre.org/package-repository") + (name 'EuAndreh) + (url "git://euandre.org/packages") (branch "main") (introduction (make-channel-introduction diff --git a/src/guix/packages.scm b/src/guix/packages.scm index 7458488..31bdaca 100644 --- a/src/guix/packages.scm +++ b/src/guix/packages.scm @@ -1,35 +1,22 @@ (define-module (packages) - #:use-module ((ice-9 textual-ports) #:prefix textual-ports:) - #:use-module ((guix licenses) #:prefix licenses:) - #:use-module ((org euandre packages) #:prefix packages:) - #:use-module ((org euandre queue) #:prefix q:) #:use-module ((org euandre packages) #:prefix pkg:) + #:use-module ((org euandre services) #:prefix serv:) #:use-module (gnu) - #:use-module (guix build-system gnu) - #:use-module (guix download) #:use-module (guix packages) #:use-module (guix utils)) -(use-package-modules - golang - golang-xyz - networking - sqlite) +(use-package-modules) -(define (slurp f) - (string-trim-both - (call-with-input-file - f - textual-ports:get-string-all))) - -(define (file name s) - (slurp (string-append "src/versions/" name "/" s))) +(define (cat name s) + (string-trim-right + (serv:slurp + (string-append "src/versions/" name "/" s)))) (define (go-latest package) (let* ((name (package-name package)) - (version (file name "version")) - (checksum (file name "sha256"))) + (version (cat name "version")) + (checksum (cat name "sha256"))) (package/inherit package (version version) (source @@ -55,12 +42,20 @@ (string-append "CC=" #$(cc-for-target))))))))) -(map go-latest - (list - pkg:gobang - pkg:golite - pkg:binder - pkg:glaze - pkg:untls - pkg:wscat - pkg:papod)) +(define-public gobang (go-latest pkg:gobang)) +(define-public golite (go-latest pkg:golite)) +(define-public binder (go-latest pkg:binder)) +(define-public glaze (go-latest pkg:glaze)) +(define-public untls (go-latest pkg:untls)) +(define-public wscat (go-latest pkg:wscat)) +(define-public papod (go-latest pkg:papod)) + + +(list + gobang + golite + binder + glaze + untls + wscat + papod) diff --git a/src/guix/services.scm b/src/guix/services.scm deleted file mode 100644 index 1e5ae4e..0000000 --- a/src/guix/services.scm +++ /dev/null @@ -1,187 +0,0 @@ -(define-module (services) - #:use-module ((ice-9 popen) #:prefix popen:) - #:use-module ((ice-9 textual-ports) #:prefix textual-ports:) - #:use-module ((gnu build linux-container) #:prefix container:) - #:use-module ((srfi srfi-1) #:prefix srfi-1:) - #:use-module ((xyz euandreh heredoc) #:prefix heredoc:) - #:use-module (gnu) - #:use-module (guix build utils) - #:use-module (guix least-authority) - #:use-module (guix records)) -(use-package-modules - admin) -(use-service-modules - admin - mcron - shepherd) -(heredoc:enable-syntax) - - -(define +working-dir+ - (if (directory-exists? "/opt/deploy/current") - "/opt/deploy/current" - (canonicalize-path "."))) - -(add-to-load-path - (string-append +working-dir+ "/src/infrastructure/guix")) -(use-modules - ((packages) #:prefix packages:)) - - - -(define-record-type* - papo-configuration - make-papo-configuration - papo-configuration? - (package papo-configuration-package (default packages:papo)) - (user papo-configuration-user (default "papo")) - (group papo-configuration-group (default "papo")) - (config-dirname papo-configuration-config (default "papo")) - (port papo-configuration-port (default 6666)) - (log-file papo-configuration-log-file (default "/var/log/papo.log")) - (data-directory papo-configuration-data-directory (default "/var/lib/papo")) - (run-directory papo-configuration-run-directory (default "/var/run/papo")) - (run-in-container? papo-configuration-run-in-container? (default #t)) - (container-name papo-configuration-container-name (default "papo-container")) - (container-namespaces papo-configuration-container-namespaces (default container:%namespaces)) - (extra-mappings papo-configuration-extra-mappings (default '()))) - -(define (papo-etc-files config) - (match-record config - () - `(("papo.json" ,(plain-file "papo.json" ""))))) - -(define (papo-log-rotations config) - (match-record config - (log-file) - (list - (log-rotation - (frequency 'weekly) - (files (list log-file)) - (options '("rotate 5200")))))) - -(define (papo-activation config) - (match-record config - (user log-file data-directory run-directory) - #~(begin - (use-modules (guix build utils)) - (format (current-error-port) - "Creating papo log directory for '~a'.~%" #$log-file) - (mkdir-p (dirname #$log-file)) - (when (not (file-exists? #$log-file)) - (call-with-output-file #$log-file (const #t))) - (chmod #$log-file #o644) - (let ((user (getpwnam #$user))) - (format (current-error-port) - "Creating papo data directory '~a'.~%" #$data-directory) - (mkdir-p #$data-directory) - (chown #$data-directory (passwd:uid user) (passwd:gid user)) - (chmod #$data-directory #o750) - (format (current-error-port) - "Creating papo run directory '~a'.~%" #$run-directory) - (mkdir-p #$run-directory) - (chown #$run-directory (passwd:uid user) (passwd:gid user)) - (chmod #$run-directory #o755))))) - -(define (papo-cronjobs _config) - (list)) - -(define (papo-accounts config) - (match-record config - (user group) - (list - (user-group - (name group) - (system? #t)) - (user-account - (name user) - (group group) - (system? #t) - (comment "The user for runtime execution of papo code") - (home-directory "/var/empty") - (shell - (file-append shadow "/sbin/nologin")))))) - -(define (wrapped-command config) - (match-record config - (package data-directory - run-in-container? container-name container-namespaces extra-mappings) - (let ((bin (file-append package "/bin/papo"))) - (if (not run-in-container?) - bin - (least-authority-wrapper - bin - #:name container-name - #:namespaces container-namespaces - #:directory data-directory - #:preserved-environment-variables - '() - #:mappings - (append - (list - (file-system-mapping - (source data-directory) - (target source) - (writable? #t)) - (file-system-mapping - (source (file-append glibc-locales "/lib/locale")) - (target "/run/current-system/locale"))) - extra-mappings)))))) - -(define (exec-action config . static-args) - (match-record config - (user group log-file data-directory) - #~(lambda dynamic-args - (fork+exec-command - (append '(#$@static-args) dynamic-args) - #:user #$user - #:group #$group - #:directory #$data-directory - #:log-file #$log-file)))) - -(define (papo-shepherd-services config) - (let ((cmd (wrapped-command config))) - (list - (shepherd-service - (provision '(papo)) - (requirement '()) - (start (exec-action config cmd "ircd")) - (stop #~(make-kill-destructor SIGKILL)) - (documentation - #"- - The Shepherd service that runs the server via "papo-ircd"."#))))) - -(define-public papo-service-type - (service-type - (name 'papo) - (extensions - (list - (service-extension shepherd-root-service-type - papo-shepherd-services) - (service-extension etc-service-type - papo-etc-files) - (service-extension profile-service-type - (compose list papo-configuration-package)) - (service-extension activation-service-type - papo-activation) - (service-extension account-service-type - papo-accounts) - (service-extension mcron-service-type - papo-cronjobs) - (service-extension rottlog-service-type - papo-log-rotations))) - (default-value (papo-configuration)) - (description - #"- - The top-level system service for papo code. - - It includes: - - the Shepherd service for starting, stopping and reloading the - service ("papo"); - - a list of cronjobs to be added to the system for sending documents - proactively; - - activation script for setting up the initial directories and permissions; - - the "papo" group and "papo" account for running the production service; - - log management (storage and rotation) for logs produced by the running services. - - The defaults of provide sane values for all of these."#))) diff --git a/src/guix/system.scm b/src/guix/system.scm index f5c2368..bb0faee 100644 --- a/src/guix/system.scm +++ b/src/guix/system.scm @@ -1,23 +1,15 @@ (use-modules - ((ice-9 textual-ports) #:prefix textual-ports:) ((srfi srfi-1) #:prefix s1:) ((xyz euandreh heredoc) #:prefix heredoc:) - ((org euandre queue) #:prefix queue:) - (gnu) - (guix build-system trivial) - (guix build utils) - (guix packages)) + ((org euandre queue) #:prefix q:) + ((org euandre services) #:prefix serv:) + (gnu)) (use-package-modules - admin - ssh version-control) (use-service-modules - admin certbot cgit - dns mail - mcron networking security ssh @@ -25,380 +17,68 @@ (heredoc:enable-syntax) -(define +ipv4+ "216.238.73.1") -(define +ipv6+ "2001:19f0:b400:1582:5400:04ff:fea9:370e") + +(define (path s) + ;; src/guix/system.scm + ../../../ = ./ + (serv:str (dirname (dirname (dirname (current-filename)))) "/" s)) (define +users+ - '(("andre" "EuAndreh" ("wheel" "become-deployer" "become-secrets-keeper")) - ("laisse" "Laísses" ()))) + `(("andre" "EuAndreh" ("wheel") ,(path "src/keys/SSH/andre.pub.txt")))) -(define +working-dir+ - (if (directory-exists? "/opt/deploy/current") - "/opt/deploy/current" - (canonicalize-path "."))) -(add-to-load-path - (string-append +working-dir+ "/src/guix")) +(add-to-load-path (dirname (current-filename))) (use-modules - ((packages) #:prefix packages:) - ((services) #:prefix services:)) - - -(define (str . rest) - (apply string-append rest)) + ((packages) #:prefix packages:)) -(define (fmt . rest) - (apply format #f rest)) - -(define (path s) - (str +working-dir+ "/" s)) - -(define (slurp s) - (call-with-input-file - s - textual-ports:get-string-all)) (define file - (compose slurp path)) + (compose serv:slurp path)) (define +tld+ - (string-trim-right - (file "src/config/tld.txt"))) - - -(define +user-accounts+ - (map (lambda (user) - (let ((name (s1:first user)) - (comment (s1:second user)) - (groups (s1:third user))) - (user-account - (name name) - (comment comment) - (group "users") - (supplementary-groups groups)))) - +users+)) - -(define (ssh-file-for user) - (let ((name (s1:first user))) - (path (fmt "src/keys/SSH/~a.pub.txt" name)))) - -(define +authorized-keys+ - (let ((users-with-keys - (map (lambda (user) - `(,@user ,(slurp (ssh-file-for user)))) - (filter (lambda (user) - (file-exists? (ssh-file-for user))) - +users+)))) - (append - (map (lambda (user) - (let ((name (s1:first user)) - (key (s1:fourth user))) - `(,name ,(plain-file (str name "-id_rsa.pub") - key)))) - users-with-keys) - `(("git" ,@(map (lambda (user) - (let ((name (s1:first user)) - (key (s1:fourth user))) - (plain-file (str name "-git-id_rsa.pub") - key))) - users-with-keys)))))) - -(define (script name content) - (package - (name name) - (version "latest") - (source #f) - (build-system trivial-build-system) - (arguments - (list - #:modules '((guix build utils)) - #:builder - #~(begin - (use-modules (guix build utils)) - (let* ((bin (string-append %output "/bin")) - (prog (string-append bin "/" #$name))) - (mkdir-p bin) - (call-with-output-file prog - (lambda (port) - (display #$content port))) - (chmod prog #o755))))) - (home-page #f) - (synopsis #f) - (description #f) - (license #f))) - - -(define ns1 (fmt "ns1.~a." +tld+)) -(define ns2 (fmt "ns2.~a." +tld+)) -(define ns ns1) -(define mail (fmt "hostmaster.~a." +tld+)) -(define dkim-selector "dkimproxyout") -(define dkim-public-key-path "/var/lib/dkimproxyout/public.key") - -(define dkim-name (str dkim-selector "._domainkey")) -(define dkim-public-key - (if (file-exists? dkim-public-key-path) - (string-join - (reverse - (cdr - (reverse - (cdr - (string-split (slurp dkim-public-key-path) - #\newline))))) - "") - "stub-public-key-for-building")) - -(define ipv4-reverse-domain - (str - (string-join (reverse - (string-split +ipv4+ - #\.)) - ".") - ".in-addr.arpa")) - -(define ipv6-reverse-domain - (str - (string-join (reverse - (map (lambda (s) (fmt "~a" s)) - (string->list - (string-delete #\: +ipv6+)))) - ".") - ".ip6.arpa")) + ((compose string-trim-right + serv:slurp + path) + "src/config/tld.txt")) -(define-zone-entries tld-zone - ("@" "" "IN" "NS" ns1) - ("@" "" "IN" "NS" ns2) - ("ns1" "" "IN" "A" +ipv4+) - ("ns1" "" "IN" "AAAA" +ipv6+) - ("ns2" "" "IN" "A" +ipv4+) - ("ns2" "" "IN" "AAAA" +ipv6+) - ("@" "" "IN" "A" +ipv4+) - ("@" "" "IN" "AAAA" +ipv6+) - ("@" "" "IN" "CAA" "0 issue \"letsencrypt.org\"") - ("@" "" "IN" "CAA" "0 issuewild \";\"") - ("@" "" "IN" "CAA" (fmt "0 iodef \"mailto:root@~a\"" +tld+)) +(define package-symbols + '()) - ("mta-sts" "" "IN" "A" +ipv4+) - ("mta-sts" "" "IN" "AAAA" +ipv6+) - ("_mta-sts" "" "IN" "TXT" "\"v=STSv1; id=20230314\"") - ("@" "" "IN" "MX" (fmt "10 ~a." +tld+)) - ("_dmarc" "" "IN" "TXT" "\"v=DMARC1; p=quarantine\"") - ("@" "" "IN" "TXT" (fmt "\"v=spf1 a:~a -all\"" +tld+)) - (dkim-name "" "IN" "TXT" (fmt "\"v=DKIM1; k=rsa; t=s; p=~a\"" dkim-public-key))) - -(define-zone-entries ipv4-reverse-domain-zone - ("@" "" "IN" "PTR" (str +tld+ ".")) - ("@" "" "IN" "NS" ns1) - ("@" "" "IN" "NS" ns2)) - -(define-zone-entries ipv6-reverse-domain-zone - ("@" "" "IN" "PTR" (str +tld+ ".")) - ("@" "" "IN" "NS" ns1) - ("@" "" "IN" "NS" ns2)) - -(define zones - (list - (knot-zone-configuration - (domain +tld+) - (semantic-checks? #t) - (zone - (zone-file - (origin +tld+) - (ns ns) - (mail mail) - (entries tld-zone)))) - (knot-zone-configuration - (domain ipv4-reverse-domain) - (semantic-checks? #t) - (zone - (zone-file - (origin ipv4-reverse-domain) - (ns ns) - (mail mail) - (entries ipv4-reverse-domain-zone)))) - (knot-zone-configuration - (domain ipv6-reverse-domain) - (semantic-checks? #t) - (zone - (zone-file - (origin ipv6-reverse-domain) - (ns ns) - (mail mail) - (entries ipv6-reverse-domain-zone)))))) - -(define private-http - '(#"- - auth_basic "Private area"; - auth_basic_user_file /opt/secrets/htpasswd.txt; - "#)) - -(define cgit-nginx-config +(define package-records (list - (list "fastcgi_param SCRIPT_FILENAME " cgit "/lib/cgit/cgit.cgi;") - #"- - fastcgi_param PATH_INFO $uri; - fastcgi_param QUERY_STRING $args; - fastcgi_param HTTP_HOST $server_name; - fastcgi_pass localhost:9000; - rewrite /git(.*) $1 break; - "#)) + #; + packages:papo.im)) ;; FIXME: move to "website" repository (operating-system (locale "en_GB.UTF-8") (timezone "America/Sao_Paulo") (host-name +tld+) - (skeletons - `((".profile" - ,(plain-file - "user-profile" - (file "src/config/profile.sh"))))) - (users - (append - (list - (user-account - (name "git") - (group "git") - (system? #t) - (comment "External SSH Git user") - (home-directory "/srv/git") - (create-home-directory? #f) - (shell - (file-append git "/bin/git-shell"))) - (user-account - (name "deployer") - (group "deployer") - (system? #t) - (comment "The account used to run deployment commands") - (home-directory "/var/empty") - (create-home-directory? #f) - (shell - (file-append shadow "/sbin/nologin"))) - (user-account - (name "secrets-keeper") - (group "secrets-keeper") - (system? #t) - (comment "The account used to manage production secrets") - (home-directory "/var/empty") - (create-home-directory? #f) - (shell - (file-append shadow "/sbin/nologin")))) - +user-accounts+ - %base-user-accounts)) - (groups - (append - (list - (user-group - (name "git") - (system? #t)) - (user-group - (name "deployer") - (system? #t)) - (user-group - (name "become-deployer") - (system? #t)) - (user-group - (name "secrets-keeper") - (system? #t)) - (user-group - (name "become-secrets-keeper") - (system? #t))) - %base-groups)) - (sudoers-file - (plain-file "sudoers" #"- - root ALL=(ALL) ALL - %wheel ALL= ALL - %become-deployer ALL=(deployer) NOPASSWD: ALL - %become-secrets-keeper ALL=(secrets-keeper) NOPASSWD: /run/current-system/profile/bin/rsync, /run/current-system/profile/bin/setfacl, /run/current-system/profile/bin/rm - git ALL= NOPASSWD: /run/current-system/profile/bin/reconfigure, /run/current-system/profile/bin/cicd - git ALL=(deployer) NOPASSWD: /run/current-system/profile/bin/rsync, /run/current-system/profile/bin/mkdir - "#)) - (packages - (append - (map - (compose list specification->package+output symbol->string) - '(nss-certs - guile-heredoc - parted - acl - bind:utils - knot:tools - file - git - lsof - moreutils - mailutils-sendmail - curl - make - borg - rsync - sqlite - strace - rlwrap - trash-cli - tree)) - (list - (script "gc" (file "src/scripts/gc.sh")) - (script "cicd" (file "src/scripts/cicd.sh")) - (script "check" (file "src/scripts/check.sh")) - (script "backup" (file "src/scripts/backup.sh")) - (script "deploy" (file "src/scripts/deploy.sh")) - (script "report" (file "src/scripts/report.sh")) - (script "cronjob" (file "src/scripts/cronjob.sh")) - (script "reconfigure" (file "src/scripts/reconfigure.sh"))) - (list - packages:papo.im) ;; FIXME: move to "website" repository - %base-packages)) + (skeletons serv:skeletons) + (users (append (serv:user-accounts +users+) %base-user-accounts)) + (packages (serv:package-set package-symbols package-records)) (services (append (list (service ntp-service-type) (service dhcp-client-service-type) - (service knot-service-type - (knot-configuration - (zones zones))) - (service openssh-service-type - (openssh-configuration - (openssh openssh-sans-x) - (password-authentication? #f) - (authorized-keys +authorized-keys+) - (extra-content #"- - ClientAliveInterval 30 - ClientAliveCountMax 20 - MaxSessions 20 - SetEnv GIT_CONFIG_GLOBAL=/etc/gitconfig - "#))) - (simple-service 'extra-rottlog-rotations rottlog-service-type - (list - (log-rotation - (frequency 'weekly) - (files '("/var/log/cronjobs.log")) - (options '("rotate 5200"))))) (service fail2ban-service-type) - (service mcron-service-type - (mcron-configuration - (jobs - (list - #~(job "0 0 * * *" "cronjob check") - #~(job "0 1 * * *" "cronjob env BORG_REPO=/mnt/backup/borg backup -q cron") - #~(job "0 2 * * *" "cronjob backup -q cron") - #~(job "0 3 * * 0" "cronjob gc") - #~(job "0 4 * * *" "cronjob reconfigure -U"))))) - (service certbot-service-type - (certbot-configuration - (email (str "root@" +tld+)) - (certificates - (list - (certificate-configuration - (domains (list +tld+)) - (deploy-hook - (program-file - "nginx-deploy-hook" - #~(let ((pid (call-with-input-file "/var/run/nginx/pid" read))) - (kill pid SIGHUP))))))))) + (service serv:binder-service-type (serv:binder-configuration (package packages:binder))) + (service serv:glaze-service-type (serv:glaze-configuration (package packages:glaze))) + (service serv:untls-service-type (serv:untls-configuration (package packages:untls))) + (service serv:wscat-service-type (serv:wscat-configuration (package packages:wscat))) + (service serv:papod-service-type (serv:papod-configuration (package packages:papod))) + (service openssh-service-type (q:openssh-default-configuration (serv:users->keys +users+))) + (service certbot-service-type (q:tld-certbot-configuration +tld+)) + (service cgit-service-type q:cgit-pre-configuration) + (service serv:syskeep-service-type) + (service q:shadow-group-service-type) + (service q:dkimproxyout-service-type) + (service q:cyrus-sasl-service-type) + (service q:dovecot-service-type) + (service q:internet-postfix-service-type) (service nginx-service-type (nginx-configuration (server-blocks @@ -407,8 +87,8 @@ (server-name (list +tld+)) (listen '("[::]:443 ssl http2" "443 ssl http2")) (root "/srv/www") - (ssl-certificate (fmt "/etc/letsencrypt/live/~a/fullchain.pem" +tld+)) - (ssl-certificate-key (fmt "/etc/letsencrypt/live/~a/privkey.pem" +tld+)) + (ssl-certificate (serv:fmt "/etc/letsencrypt/live/~a/fullchain.pem" +tld+)) + (ssl-certificate-key (serv:fmt "/etc/letsencrypt/live/~a/privkey.pem" +tld+)) (locations (list (nginx-location-configuration @@ -417,26 +97,27 @@ (list ;; FIXME: use this for blue/green deployment #; - (fmt "include /var/run/~a/curr.conf;~%" +tld+)))) + (serv:fmt "include /var/run/~a/curr.conf;~%" +tld+)))) (nginx-location-configuration (uri "/git/static/") (body (list (list "alias " cgit "/share/cgit/;")))) - (nginx-location-configuration - (uri "/git/private/") - (body - (append - cgit-nginx-config - private-http))) (nginx-location-configuration (uri "/git/") - (body cgit-nginx-config)))) + (body + (list + (list "fastcgi_param SCRIPT_FILENAME " cgit "/lib/cgit/cgit.cgi;") + #"- + fastcgi_param PATH_INFO $uri; + fastcgi_param QUERY_STRING $args; + fastcgi_param HTTP_HOST $server_name; + fastcgi_pass localhost:9000; + rewrite /git(.*) $1 break; + "#))))) (raw-content '(#"- - # BearSSL still doesn't do TLSv1.3, so we deem TLSv1.2 as - # acceptable - ssl_protocols TLSv1.2 TLSv1.3; + ssl_protocols TLSv1.3; ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH; ssl_prefer_server_ciphers on; gzip off; # Disable catch-all compression due to BREACH @@ -444,35 +125,10 @@ autoindex on; add_header Strict-Transport-Security 'max-age=31536000; includeSubdomains' always; "#))))))) - (service cgit-service-type queue:cgit-pre-configuration) - (simple-service 'extra-etc-file etc-service-type - `(("rc" ,(plain-file "rc.sh" (file "src/config/rc.sh"))) - ("known_hosts" ,(plain-file "known_hosts" (file "src/config/known_hosts.txt"))) - ("id_rsa.pub" ,(plain-file "id_rsa.pub" (file (fmt "src/keys/SSH/root@~a.id_rsa.pub.stripped" +tld+)))) - ("ssh.conf" ,(plain-file "ssh.conf" (file "src/config/ssh.conf"))) - ("init.scm" ,(plain-file "init.scm" (file "src/config/init.scm"))) - ("conf.env" ,(plain-file "conf.env" (file "src/config/conf.env"))) - ("gitconfig" ,(plain-file "gitconfig" (file "src/config/gitconfig"))))) - (service queue:shadow-group-service-type) - (service queue:dkimproxyout-service-type) - (service queue:cyrus-sasl-service-type) - (service queue:dovecot-service-type) - (service queue:internet-postfix-service-type - (queue:postfix-configuration - (enable-submission? #t) - (main.cf-extra #"- - message_size_limit = 102400000 - mailbox_size_limit = 5120000000 - "#))) (service mail-aliases-service-type `(("root" "andre") ("support" ,@(map s1:first +users+))))) - (modify-services %base-services - (rottlog-service-type config => - (rottlog-configuration - (inherit config) - (rc-file - (file-append queue:rottlog-mailutils-sendmail "/etc/rc"))))))) + serv:base-services)) (bootloader (bootloader-configuration (bootloader grub-bootloader) diff --git a/src/scripts/backup.sh b/src/scripts/backup.sh deleted file mode 100755 index 6a2a4ff..0000000 --- a/src/scripts/backup.sh +++ /dev/null @@ -1,149 +0,0 @@ -#!/bin/sh -set -eu - -usage() { - cat <<-'EOF' - Usage: - backup [-q] [-C COMMENT] [-x] [ARCHIVE_TAG] - backup -h - EOF -} - -help() { - cat <<-'EOF' - - - Options: - -q disable verbose mode, useful for batch sessions - -C COMMENT the comment text to be attached to the archive - -x disable checking the repository after creating the backup - -h, --help show this message - - ARCHIVE_TAG the tag used to create the new - backup (default: "manual") - - - The repository is expected to have been create with: - - $ borg init -e repokey-blake2 - - The following environment variables are expected to be exported: - - $BORG_PASSCOMMAND - $BORG_REPO - $BORG_REMOTE_PATH - - Password-less SSH access is required, usually done via adding - /root/.ssh/id_rsa.pub to the ssh remote's - $THE_REMOTE:.ssh/authorized_keys - - Root permission is also required. - - - Examples: - - Run backup from cronjob: - - $ backup - - - Run backup from cronjob: - - $ backup -q cronjob - - - Create backup with a comment, a tag, and verbose mode active, and do not - verify the repository afterwards: - - $ backup -xC 'The backup has a comment' - EOF -} - - -for flag in "$@"; do - case "$flag" in - --) - break - ;; - --help) - usage - help - exit - ;; - *) - ;; - esac -done - -VERBOSE_FLAGS='--verbose --progress' -COMMENT=' ' -CHECK=true -while getopts 'qC:xh' flag; do - case "$flag" in - q) - VERBOSE_FLAGS='' - ;; - C) - COMMENT="$OPTARG" - ;; - x) - CHECK=false - ;; - h) - usage - help - exit - ;; - *) - usage >&2 - exit 2 - ;; - esac -done -shift $((OPTIND - 1)) - -ARCHIVE_TAG="${1:-manual}" - - -if [ "$(id -un)" != 'root' ]; then - printf 'This script must be run as root.\n\n' >&2 - usage >&2 - exit 2 -fi - - -run() { - STATUS=0 - set -x - # shellcheck disable=2086 - sudo -i borg create \ - $VERBOSE_FLAGS \ - --comment "$COMMENT" \ - --stats \ - --compression lzma,9 \ - "$BORG_REPO::$(hostname)-{now}-$ARCHIVE_TAG" \ - /mnt/production/ \ - /root/ \ - /home/ \ - /etc/ \ - /var/ \ - /opt/ \ - /srv/ || STATUS=$? - set +x - - if [ "$STATUS" = 0 ]; then - return 0 - elif [ "$STATUS" = 1 ]; then - printf 'WARNING, but no ERROR.\n' >&2 - return 0 - else - return "$STATUS" - fi -} - -run - -if [ "$CHECK" = true ]; then - # shellcheck disable=2086 - sudo -i borg check $VERBOSE_FLAGS --verify-data "$BORG_REPO" -fi diff --git a/src/scripts/check.sh b/src/scripts/check.sh deleted file mode 100755 index 5c63816..0000000 --- a/src/scripts/check.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/sh -set -eu - -usage() { - cat <<-'EOF' - Usage: - check - check -h - EOF -} - -help() { - cat <<-'EOF' - - - Options: - -h, --help show this message - - - Run system sanity checks, such as email reachability, alarms - reachability, filesystem checks, etc. - - - Examples: - - Just run it - - $ check - EOF -} - - -for flag in "$@"; do - case "$flag" in - --) - break - ;; - --help) - usage - help - exit - ;; - *) - ;; - esac -done - -while getopts 'h' flag; do - case "$flag" in - h) - usage - help - exit - ;; - *) - usage >&2 - exit 2 - ;; - esac -done -shift $((OPTIND - 1)) - - -if [ "$(id -un)" != 'root' ]; then - printf 'This script must be run as root.\n\n' >&2 - usage >&2 - exit 2 -fi - - -uuid() { - od -xN20 /dev/urandom | - head -n1 | - awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}' -} - -for alias in abuse admin postmaster hostmaster; do - uuid | mail -s "\"$alias\" alias test from $(id -un)@$(hostname)" "$alias@$(hostname)" -done - - -PARTITIONS=' -/dev/vda3 -/dev/vdb1 -/dev/vdc1 -' -set -x - -for part in $PARTITIONS; do - btrfs scrub start -B "$part" - btrfs check --force -p "$part" -done diff --git a/src/scripts/cicd.sh b/src/scripts/cicd.sh deleted file mode 100755 index 58f4fce..0000000 --- a/src/scripts/cicd.sh +++ /dev/null @@ -1,168 +0,0 @@ -#!/bin/sh -set -eu - -usage() { - cat <<-'EOF' - Usage: - cicd [-n] NAME [SHA] - cicd -h - EOF -} - -help() { - cat <<-'EOF' - - - Options: - -n build the system, but don't switch to it (dry-run) - -h, --help show this message - - NAME the name of the project - SHA the repository SHA to checkout (default: main) - - - Do a CI/CD run of the project called NAME, located at - /srv/git/$NAME.git. If -n is given, only build, otherwise - also do the deploy when the build is successfull. - - A "build" consists of: - - doing a fresh clone of the project on a temporary directory; - - checkout the project to version $SHA; - - when a "manifest.scm" file exists in the root of the project, - use it to launch a containerized Guix shell; otherwise use a - fallback template for a containerized Guix shell; - - build the "dev" target of the Makefile via "make dev". - - A "deploy" consists of: - - copying the "description" metadata file to - /srv/git/$NAME.git/description, in order to update the project - repository's description; - - upgrading the "pre-receive" Git hook, so that future runs are - affected by it; - - copying the "public/" directory that the "dev" target built to - the /srv/www/s/$NAME/ directory, so that the projects "public/" - directory is accessible via the web address - "https://euandre.org/s/$NAME/". - - This command must be ran as root. - - - Examples: - - Build and deploy the "remembering" project on the default branch: - - $ sudo cicd remembering - - - Build the "urubu" project on a specific commit, but don't deploy: - - $ sudo cicd -n urubu 916dafc092f797349a54515756f2c8e477326511 - EOF -} - - -for flag in "$@"; do - case "$flag" in - --) - break - ;; - --help) - usage - help - exit - ;; - *) - ;; - esac -done - -DRY_RUN=false -while getopts 'nh' flag; do - case "$flag" in - n) - DRY_RUN=true - ;; - h) - usage - help - exit - ;; - *) - usage >&2 - exit 2 - ;; - esac -done -shift $((OPTIND - 1)) - -NAME="${1:-}" -SHA="${2:-main}" -REPO="/srv/git/$NAME.git" - -if [ -z "$NAME" ]; then - printf 'Missing NAME.\n\n' >&2 - usage >&2 - exit 2 -fi - -if [ "$(id -un)" != 'root' ]; then - printf 'This script must be run as root.\n\n' >&2 - usage >&2 - exit 2 -fi - - -set +eu -# shellcheck source=/dev/null -. /etc/rc -set -eu - - -uuid() { - od -xN20 /dev/urandom | - head -n1 | - awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}' -} - -tmpname() { - printf '%s/uuid-tmpname with spaces.%s' "${TMPDIR:-/tmp}" "$(uuid)" -} - -mkdtemp() { - name="$(tmpname)" - mkdir -- "$name" - printf '%s' "$name" -} - - -TMP="$(mkdtemp)" -trap 'rm -rf "$TMP"' EXIT - - -set -x -chown deployer:deployer "$TMP" -cd "$TMP" -sudo -u deployer git clone "$REPO" . -sudo -u deployer --preserve-env=GIT_CONFIG_GLOBAL git checkout "$SHA" -guix system describe - -if [ -f manifest.scm ]; then - guix shell -Cv3 -m manifest.scm -- make dev -else - guix shell -Cv3 -- make dev -fi - -if [ "$DRY_RUN" = false ]; then - # COMMENT: pre-receive is always running the previous version! - # The same is true for the reconfigure script itself. - sudo cp description "$REPO"/description - sudo cp aux/ci/git-pre-receive.sh "$REPO"/hooks/pre-receive - - sudo -u deployer rsync \ - --delete \ - --chmod=D775,F664 \ - --chown=deployer:deployer \ - --exclude 'ci/*' \ - -a \ - public/ /srv/www/s/"$NAME"/ -fi diff --git a/src/scripts/cronjob.sh b/src/scripts/cronjob.sh deleted file mode 100755 index 4cd456e..0000000 --- a/src/scripts/cronjob.sh +++ /dev/null @@ -1,169 +0,0 @@ -#!/bin/sh -set -eu - -usage() { - cat <<-'EOF' - Usage: - cronjob COMMAND... - cronjob -h - EOF -} - -help() { - cat <<-'EOF' - - - Options: - -h, --help show this message - - COMMAND the command to be executed - - - Execute the given command, and send the output to email, with - special treatment to the status code. It kills the job it it - lasts more than one hour. - - It load the appropriate files, so that the actual cron - declaration is smaller. - - - Examples: - - Run a backup: - - $ cronjob backup -q cron - EOF -} - - -for flag in "$@"; do - case "$flag" in - --) - break - ;; - --help) - usage - help - exit - ;; - *) - ;; - esac -done - -while getopts 'h' flag; do - case "$flag" in - h) - usage - help - exit - ;; - *) - usage >&2 - exit 2 - ;; - esac -done -shift $((OPTIND - 1)) - -if [ -z "${1:-}" ]; then - printf 'Missing COMMAND.\n\n' >&2 - usage >&2 - exit 2 -fi - -if [ "$(id -un)" != 'root' ]; then - printf 'This script must be run as root.\n\n' >&2 - usage >&2 - exit 2 -fi - - -set +eu -# shellcheck source=/dev/null -. /etc/rc -set -eu - - - -epoch() { - awk 'BEGIN { srand(); print(srand()); }' -} - -now() { - date '+%Y-%m-%dT%H:%M:%S%:z' -} - - -uuid() { - od -xN20 /dev/random | - head -n1 | - awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}' -} - -mkstemp() { - name="${TMPDIR:-/tmp}/uuid-tmpname with spaces.$(uuid)" - touch "$name" - printf '%s' "$name" -} - -pre() { - # Same as: - # sed -u "s|^|[$CMD]: |" - # but the "-u" option is not POSIX - IFS='' - while read -r line; do - printf '[%s]: %s\n' "$CMD" "$line" - done -} - -duration() { - minutes=$((${1} / 60)) - seconds=$((${1} % 60)) - printf '%sm%ss' "$minutes" "$seconds" -} - - -CMD="$*" -HOSTNAME="$(hostname)" -FROM="cronjob@$HOSTNAME" -TIMEOUT='10800' # three hours -STATUS_F="$(mkstemp)" -OUT="$(mkstemp)" - -email() { - { - cat <<-EOF - Content-Type: text/plain; charset=UTF-8 - Content-Transfer-Encoding: 8bit - From: $FROM - To: root@localhost - Subject: (exit status: $(cat "$STATUS_F")) - $HOSTNAME: $CMD - - EOF - cat "$OUT" - } | sendmail -t -f "$FROM" - rm -f "$OUT" "$STATUS_F" -} -trap email EXIT - -{ - cat <<-EOF - Running commad: $CMD - Starting at: $(now) - - EOF - - START="$(epoch)" - STATUS=0 - timeout "$TIMEOUT" "$@" || STATUS=$? - printf '%s' "$STATUS" > "$STATUS_F" - END="$(epoch)" - DURATION_SECONDS=$((END - START)) - - cat <<-EOF - - Finished at: $(now) - Duration: $(duration "$DURATION_SECONDS") - EOF -} 2>&1 | pre | ts '%Y-%m-%dT%H:%M:%S' | tee "$OUT" >> /var/log/cronjobs.log diff --git a/src/scripts/deploy.sh b/src/scripts/deploy.sh deleted file mode 100755 index dc30484..0000000 --- a/src/scripts/deploy.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/sh -set -eu - -usage() { - cat <<-'EOF' - Usage: - deploy - deploy -h - EOF -} - -help() { - cat <<-'EOF' - - - Options: - -h, --help show this message - - - Do a blue/green deployment of the main service. It makes sure - that the new service is up and running before shutting down the - old one. - - - Examples: - - Just do the deploy: - - $ deploy - EOF -} - - -for flag in "$@"; do - case "$flag" in - --) - break - ;; - --help) - usage - help - exit - ;; - *) - ;; - esac -done - -while getopts 'h' flag; do - case "$flag" in - h) - usage - help - exit - ;; - *) - usage >&2 - exit 2 - ;; - esac -done -shift $((OPTIND - 1)) - - -if [ "$(id -un)" != 'root' ]; then - printf 'This script must be run as root.\n\n' >&2 - usage >&2 - exit 2 -fi - - -echo FIXME diff --git a/src/scripts/gc.sh b/src/scripts/gc.sh deleted file mode 100755 index e037f3c..0000000 --- a/src/scripts/gc.sh +++ /dev/null @@ -1,146 +0,0 @@ -#!/bin/sh -set -eu - -usage() { - cat <<-'EOF' - Usage: - gc [TYPE] - gc -h - EOF -} - -help() { - cat <<-'EOF' - - - Options: - -h, --help show this message - - TYPE what to do GC on (default: all): - - guix - - deploy - - trash - - tmpdir - - logs - - - GC the server, deleting old, unusable data, in order to free - disk space system-wide. - - - Examples: - - Just run it, for all: - - $ gc - - - Cleanup tmpdir: - - $ gc tmpdir - EOF -} - - -for flag in "$@"; do - case "$flag" in - --) - break - ;; - --help) - usage - help - exit - ;; - *) - ;; - esac -done - -while getopts 'h' flag; do - case "$flag" in - h) - usage - help - exit - ;; - *) - usage >&2 - exit 2 - ;; - esac -done -shift $((OPTIND - 1)) - - -if [ "$(id -un)" != 'root' ]; then - printf 'This script must be run as root.\n\n' >&2 - usage >&2 - exit 2 -fi - - -disk() { - df -h / /mnt/backup/ | - tail -n +2 | - awk '{ printf "%s\t%s/%s\t%s\n", $4, $3, $2, $6 }' -} - -today() { - date '+%Y-%m-%d' -} - -gc_guix() { - sudo -i guix system delete-generations 1m - sudo -i guix gc -d 1m -} - -gc_deploy() { - find /opt/deploy \ - ! -path /opt/deploy -prune \ - -type d \ - -not -name "$(today)*" \ - -exec rm -rvf "{}" ';' -} - -gc_trash() { - yes | sudo -i trash-empty -} - -gc_tmpdir() { - find "${TMPDIR:-/tmp}" -atime +10 -exec rm -vf "{}" ';' -} - -gc_logs() { - find /var/log/ci/ -atime +10 -exec rm -vf "{}" ';' -} - - -gc_all() { - gc_guix - gc_deploy - gc_trash - gc_tmpdir - gc_logs -} - - -TYPE="${1:-all}" -CMD=gc_"$TYPE" -if ! command -v "$CMD" >/dev/null; then - printf 'Invalid TYPE: "%s".\n\n' "$TYPE" >&2 - usage >&2 - exit 2 -fi - -BEFORE="$(disk)" -set -x -"$CMD" -set +x -AFTER="$(disk)" - -cat <<-EOF - Disk space: - before: $BEFORE - after: $AFTER -EOF diff --git a/src/scripts/reconfigure.sh b/src/scripts/reconfigure.sh deleted file mode 100755 index 08585b3..0000000 --- a/src/scripts/reconfigure.sh +++ /dev/null @@ -1,146 +0,0 @@ -#!/bin/sh -set -eu - -usage() { - cat <<-'EOF' - Usage: - reconfigure [-n] [-U] [SHA] - reconfigure -h - EOF -} - -help() { - cat <<-'EOF' - - - Options: - -n build the system, but don't switch to it (dry-run) - -U pull the latest channels before reconfiguring - -h, --help show this message - - SHA the repository SHA to checkout (default: main) - - - Run a "guix system reconfigure" as root via "sudo -i". If a -U - flag is given, perform a "guix pull" (in root profile) prior to - the reconfigure. The user must be able to become the "deployer" - user, either via "sudo reconfigure" or by being member of the - "become-deployer" group. - - - Examples: - - Reconfigure the system: - - $ reconfigure - - - Build the system on a custom SHA, but don't switch to it: - - $ reconfigure -n 916dafc092f797349a54515756f2c8e477326511 - - - Update and upgrade: - - $ reconfigure -U - EOF -} - - -for flag in "$@"; do - case "$flag" in - --) - break - ;; - --help) - usage - help - exit - ;; - *) - ;; - esac -done - -UPDATE=false -DRY_RUN=false -while getopts 'nUh' flag; do - case "$flag" in - n) - DRY_RUN=true - ;; - U) - UPDATE=true - ;; - h) - usage - help - exit - ;; - *) - usage >&2 - exit 2 - ;; - esac -done -shift $((OPTIND - 1)) - -# shellcheck source=/dev/null -. /etc/conf.env -SHA="${1:-main}" -REPO="/srv/git/$REPO_NAME" -NOW="$(date '+%Y-%m-%dT%H:%M:%S%:z')" -NOW_DIR=/opt/deploy/"$NOW" -NPROC=$(($(nproc) * 2 + 1)) - - -if [ "$(id -un)" != 'root' ]; then - printf 'This script must be run as root.\n\n' >&2 - usage >&2 - exit 2 -fi - - -set +eu -# shellcheck source=/dev/null -. /etc/rc -set -eu - - -if [ "$UPDATE" = true ] && [ "$DRY_RUN" = false ]; then - sudo -i guix pull -v3 -fi - -set -x -sudo -u deployer git clone --depth=1 "file://$REPO" "$NOW_DIR" -sudo -u deployer rm -f /opt/deploy/current -sudo -u deployer ln -rs "$NOW_DIR" /opt/deploy/current -cd /opt/deploy/current -sudo -u deployer git fetch --depth=1 "file://$REPO" "$SHA" -sudo -u deployer --preserve-env=GIT_CONFIG_GLOBAL git checkout "$SHA" -guix system describe - -if [ "$DRY_RUN" = true ]; then - sudo -i guix system -c$NPROC -v3 build "$PWD"/src/guix/system.scm -else - # COMMENT: pre-receive is always running the previous version! - # The same is true for the reconfigure script itself. - cp description "$REPO"/description - cp src/ci/git-pre-receive.sh "$REPO"/hooks/pre-receive - cp src/guix/channels.scm /etc/guix/ - cp src/guix/system.scm /etc/guix/ - - sudo -i guix system -c$NPROC -v3 reconfigure /etc/guix/system.scm - - sudo -u deployer rsync \ - --delete \ - --chmod=D775,F664 \ - --chown=deployer:deployer \ - --exclude "$CI_SUFFIX/*" \ - -a \ - /run/current-system/profile/share/doc/"$NAME"/ "$HTML_OUTDIR_TOP"/ - - ln -sf /var/log/"$NAME".log "$HTML_OUTDIR_PRIV" - - deploy -fi diff --git a/src/scripts/report.sh b/src/scripts/report.sh deleted file mode 100755 index d9f0786..0000000 --- a/src/scripts/report.sh +++ /dev/null @@ -1,260 +0,0 @@ -#!/bin/sh -set -eu - -usage() { - cat <<-'EOF' - Usage: - report [-C REPO] -o DIRECTORY - report -h - EOF -} - -help() { - cat <<-'EOF' - - - Options: - -C REPO change to REPO when doing Git operations (default: $PWD) - -o DIRECTORY the directory where to place the generated files - -h, --help show this message - - - Gather data from Git Notes, and generate an HTML report on CI runs. - - Two refs with notes are expected: - 1. refs/notes/ci-data: contains metadata abount the CI runs, - with timestamps, filenames and exit status; - 2. refs/notes/ci-logs: contains the content of the log. - - When reconstructing the CI run, the $FILENAME present in - the refs/notes/ci-data ref names the file, and its content comes - from refs/notes/ci-logs. - - On a CI run that generated the numbers from 1 to 10, for a file named - 'my-ci-run-2020-01-01-deadbeef.log' that exited successfully, the - expected output on the target directory "public" is: - - $ tree public/ - public/ - index.html - data/ - my-ci-run-2020-01-01-deadbeef.log - ... - logs/ - my-ci-run-2020-01-01-deadbeef.log - ... - - $ cat public/data/my-ci-run-2020-01-01-deadbeef.log - 0 deadbeef my-ci-run-2020-01-01-deadbeef.log - - $ cat public/logs/my-ci-run-2020-01-01-deadbeef.log - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - - The generated 'index.html' is a webpage with the list of all known - CI runs, their status, a link to the commit and a link to the - log file. - - To enable fetching these refs by default, do so in the git config: - - $ git config --add remote.origin.fetch '+refs/notes/*:refs/notes/*' - - - Examples: - - Generate the report on the 'www' directory: - - $ report -o www - EOF -} - - -for flag in "$@"; do - case "$flag" in - --) - break - ;; - --help) - usage - help - exit - ;; - *) - ;; - esac -done - -REPO="$PWD" -while getopts 'C:o:h' flag; do - case "$flag" in - C) - REPO="$OPTARG" - ;; - o) - OUTDIR="$OPTARG" - ;; - h) - usage - help - exit - ;; - *) - exit 2 - ;; - esac -done -shift $((OPTIND - 1)) - -if [ -z "${OUTDIR:-}" ]; then - printf 'Missing -o OUTDIR.\n\n' >&2 - usage >&2 - exit 2 -fi - -if [ -r src/config/conf.env ]; then - CONF=src/config/conf.env -else - CONF=/etc/conf.env -fi - -# shellcheck source=/dev/null -. "$CONF" - - -esc() { - sed \ - -e 's|&|\&|g' \ - -e 's|<|\<|g' \ - -e 's|>|\>|g' \ - -e 's|"|\"|g' \ - -e "s|'|\'|g" -} - -mkdir -p "$OUTDIR" -cd "$OUTDIR" -mkdir -p logs data - -for c in $(git -C "$REPO" notes list | cut -d' ' -f2); do - git -C "$REPO" notes --ref=refs/notes/ci-data show "$c" > data/FILENAME-tmp - FILENAME="$(grep '^filename ' data/FILENAME-tmp | cut -d' ' -f2-)" - mv data/FILENAME-tmp data/"$FILENAME" - git -C "$REPO" notes --ref=refs/notes/ci-logs show "$c" > logs/"$FILENAME" -done - -{ - cat <<-EOF - - - - - - - - $NAME - CI logs - - - -
-

- CI logs for - $NAME -

-
    - EOF - - - PASS='✅' # ✅ - WARN='🐌' # 🐌 - FAIL='❌' # ❌ - find data/ -type f | LANG=C.UTF-8 sort -r | while read -r f; do - STATUS="$( grep '^status ' "$f" | cut -d' ' -f2- | esc)" - SHA="$( grep '^sha ' "$f" | cut -d' ' -f2- | esc)" - FILENAME="$(grep '^filename ' "$f" | cut -d' ' -f2- | esc)" - DURATION="$(grep '^duration ' "$f" | cut -d' ' -f2- | cut -d'"' -f1 | esc)" - MESSAGE="$({ - git -C "$REPO" log -1 --format=%B "$SHA" || { - git fetch origin "$SHA" - git -C "$REPO" log -1 --format=%B "$SHA" - } - } | esc)" - - if [ "$STATUS" = 0 ]; then - if [ "$DURATION" -le 60 ]; then - STATUS_MARKER="$PASS" - else - STATUS_MARKER="$WARN" - fi - else - STATUS_MARKER="$FAIL" - fi - - cat <<-EOF -
  1. -
    #
    - $STATUS_MARKER -
    ${DURATION:-?}s
    -
    (commit)
    -
    $FILENAME
    -
    (data)
    -
    -
    $MESSAGE
    -
  2. - EOF - done - - cat <<-EOF -
-
- - - EOF -} > index.html -- cgit v1.2.3