From cf8a3f15ee1c80b874be10cbdd34496b84560f59 Mon Sep 17 00:00:00 2001 From: Kévin Le Gouguec Date: Tue, 14 Jan 2025 22:48:35 +0100 Subject: Sort guides up a bit --- guides/applications/emacs/AUCTeX.org | 24 ++++ guides/applications/emacs/development.org | 121 ++++++++++++++++++ guides/applications/emacs/elisp-demos.org | 28 +++++ guides/applications/emacs/use-package.org | 48 ++++++++ guides/applications/emacs/windows.org | 25 ++++ guides/applications/ffmpeg.org | 22 ++++ guides/applications/git.org | 40 ++++++ guides/applications/imagemagick.org | 81 ++++++++++++ guides/cloud/vps.org | 198 ------------------------------ guides/communication/jargon.org | 13 ++ guides/communication/writing.md | 45 +++++++ guides/emacs/AUCTeX.org | 24 ---- guides/emacs/development.org | 121 ------------------ guides/emacs/elisp-demos.org | 28 ----- guides/emacs/use-package.org | 48 -------- guides/emacs/windows.org | 25 ---- guides/ffmpeg.org | 22 ---- guides/git.org | 40 ------ guides/imagemagick.org | 81 ------------ guides/jargon.org | 13 -- guides/languages/python.org | 40 ++++++ guides/languages/rust/error-handling.org | 16 +++ guides/languages/shell-101.org | 44 +++++++ guides/mail.org | 27 ---- guides/python.org | 40 ------ guides/rust/error-handling.org | 16 --- guides/services/mail.org | 27 ++++ guides/shell-101.org | 44 ------- guides/sysadmin/cloud/vps.org | 198 ++++++++++++++++++++++++++++++ guides/writing.md | 45 ------- 30 files changed, 772 insertions(+), 772 deletions(-) create mode 100644 guides/applications/emacs/AUCTeX.org create mode 100644 guides/applications/emacs/development.org create mode 100644 guides/applications/emacs/elisp-demos.org create mode 100644 guides/applications/emacs/use-package.org create mode 100644 guides/applications/emacs/windows.org create mode 100644 guides/applications/ffmpeg.org create mode 100644 guides/applications/git.org create mode 100644 guides/applications/imagemagick.org delete mode 100644 guides/cloud/vps.org create mode 100644 guides/communication/jargon.org create mode 100644 guides/communication/writing.md delete mode 100644 guides/emacs/AUCTeX.org delete mode 100644 guides/emacs/development.org delete mode 100644 guides/emacs/elisp-demos.org delete mode 100644 guides/emacs/use-package.org delete mode 100644 guides/emacs/windows.org delete mode 100644 guides/ffmpeg.org delete mode 100644 guides/git.org delete mode 100644 guides/imagemagick.org delete mode 100644 guides/jargon.org create mode 100644 guides/languages/python.org create mode 100644 guides/languages/rust/error-handling.org create mode 100644 guides/languages/shell-101.org delete mode 100644 guides/mail.org delete mode 100644 guides/python.org delete mode 100644 guides/rust/error-handling.org create mode 100644 guides/services/mail.org delete mode 100644 guides/shell-101.org create mode 100644 guides/sysadmin/cloud/vps.org delete mode 100644 guides/writing.md (limited to 'guides') diff --git a/guides/applications/emacs/AUCTeX.org b/guides/applications/emacs/AUCTeX.org new file mode 100644 index 0000000..0082759 --- /dev/null +++ b/guides/applications/emacs/AUCTeX.org @@ -0,0 +1,24 @@ +* Completion for cross-references +To integrate AUCTeX with reftex and get completion for ~\cite~ +(bibliography) and ~\ref~ (intra-document labels): +#+begin_src elisp +;; In init file: +(put 'TeX-auto-save 'safe-local-variable 'booleanp) +(put 'TeX-parse-self 'safe-local-variable 'booleanp) +(add-hook 'LaTeX-mode-hook 'turn-on-reftex) +(setq reftex-plug-into-AUCTeX t) +(setq reftex-label-illegal-re "[^-a-zA-Z0-9_+=:;,./]") ; Allow slashes. +(setq TeX-auto-local ".auctex") + +;; Top-level .dir-locals.el: +((latex-mode + . ((TeX-auto-save . t) + (TeX-parse-self . t))) + (bibtex-mode + . ((TeX-auto-save . t) + (TeX-parse-self . t)))) + +;; .dir-locals.el files in subfolders: +((latex-mode + . ((TeX-master . "../path/to/main.tex")))) +#+end_src diff --git a/guides/applications/emacs/development.org b/guides/applications/emacs/development.org new file mode 100644 index 0000000..f1363e1 --- /dev/null +++ b/guides/applications/emacs/development.org @@ -0,0 +1,121 @@ +* Build +** bootstrapping +Libraries that are part of the "bootstrap set" cannot rely on +autoloading. Example: =files.el= must ~(eval-when-compile (require +'pcase))~, even though it only uses autoloaded macros. +* Tests +** Running +Somehow I can never remember how to ask =make= how to run a specific +set of tests. + +tl;dr, running =$testcases_regexp= from =test/$testfile.el=, to test +changes in =lisp/$sourcefile.el=: +#+begin_src sh +make -C test $testfile \ + TEST_LOAD_EL=yes \ + TEST_BACKTRACE_LINE_LENGTH=nil \ + SELECTOR="\"$testcases_regexp\"" \ + EMACS_EXTRAOPT="-l ../lisp/$sourcefile.el" +#+end_src + +Relevant bits from test/Makefile: + +#+begin_src makefile +## filename.log: run tests from filename.el(c) if .log file needs updating +## filename: re-run tests from filename.el(c), with no logging +#+end_src + +#+begin_src makefile +# Whether to run tests from .el files in preference to .elc, we do +# this by default since it gives nicer stacktraces. +# If you just want a pass/fail, setting this to no is much faster. +export TEST_LOAD_EL ?= \ + $(if $(findstring $(MAKECMDGOALS), all check check-maybe),no,yes) +#+end_src + +#+begin_src makefile +TEST_RUN_ERT = --batch --eval '(ert-run-tests-batch-and-exit (quote ${SELECTOR_ACTUAL}))' ${WRITE_LOG} +#+end_src + +#+begin_src makefile +ifdef SELECTOR +SELECTOR_ACTUAL=$(SELECTOR) +#+end_src + +Selector documentation from ~ert-select-tests~: +#+begin_quote +Valid SELECTORs: + +nil -- Selects the empty set. +t -- Selects UNIVERSE. +:new -- Selects all tests that have not been run yet. +:failed, :passed -- Select tests according to their most recent result. +:expected, :unexpected -- Select tests according to their most recent result. +a string -- A regular expression selecting all tests with matching names. +a test -- (i.e., an object of the ert-test data-type) Selects that test. +a symbol -- Selects the test that the symbol names, signals an + ‘ert-test-unbound’ error if none. +(member TESTS...) -- Selects the elements of TESTS, a list of tests + or symbols naming tests. +(eql TEST) -- Selects TEST, a test or a symbol naming a test. +(and SELECTORS...) -- Selects the tests that match all SELECTORS. +(or SELECTORS...) -- Selects the tests that match any of the SELECTORS. +(not SELECTOR) -- Selects all tests that do not match SELECTOR. +(tag TAG) -- Selects all tests that have TAG on their tags list. + A tag is an arbitrary label you can apply when you define a test. +(satisfies PREDICATE) -- Selects all tests that satisfy PREDICATE. + PREDICATE is a function that takes an ert-test object as argument, + and returns non-nil if it is selected. +#+end_quote +** Writing +*** Mocking functions +#+begin_src elisp +(cl-letf (((symbol-function 'read-something) + (lambda (&rest _) "result"))) + (some-command)) +#+end_src + +* Third-party packages +** forge +*** build +Use =config.mk= to set =LOAD_PATH=: +#+begin_src makefile +find-lib = $(dir $(shell emacs -Q -batch \ + -eval "(require 'find-func)" \ + -eval "(package-initialize)" \ + -eval "(require '$(1))" \ + -eval "(princ (find-library-name \"$(1)\"))")) + +sources = ~/src/emacs/magit/lisp + +elpa = \ + closql \ + compat \ + dash \ + emacsql \ + ghub \ + markdown-mode \ + transient \ + treepy \ + with-editor \ + yaml +elpa_dirs = $(foreach d,$(elpa),$(call find-lib,$(d))) + +LOAD_PATH = $(addprefix -L ,$(elpa_dirs) $(sources)) +#+end_src + +*** load sources +~emacs -Q -L ./lisp~ does not seem to work: after running +~(package-initialize)~, =M-x find-library forge= returns the instance +installed under ~package-user-dir~. + +This can be circumvented with ~(setq pacakge-load-list '((forge nil) +all))~. + +Full command line for easy startup: + +#+begin_src sh +emacs -Q -L ./lisp \ + -eval "(setq pacakge-load-list '((forge nil) all))" \ + -f package-initialize \ +#+end_src diff --git a/guides/applications/emacs/elisp-demos.org b/guides/applications/emacs/elisp-demos.org new file mode 100644 index 0000000..029f5be --- /dev/null +++ b/guides/applications/emacs/elisp-demos.org @@ -0,0 +1,28 @@ +Some terse "context-free" Emacs Lisp snippets that hopefully speak for +themselves. + +* The prefix argument +#+begin_src elisp +(defun my/prefix-arg-demo (arg) + (interactive (list current-prefix-arg)) + (insert + (let ((num (prefix-numeric-value arg))) + (format "%s %d %s\n" + arg + num + (cond ((null arg) "N/A") + ((numberp arg) (format "M-%d or C-u %d" arg arg)) + ((equal arg '-) (format "M-- or C-u -")) + ((listp arg) + (if (< num 0) + (format "(M-- or C-u -) C-u × %d" (log (- num) 4)) + (format "C-u × %d" (log num 4))))))))) +#+end_src +#+begin_example +nil 1 N/A +2 2 M-2 or C-u 2 +- -1 M-- or C-u - +(-16) -16 (M-- or C-u -) C-u × 2 +(64) 64 C-u × 3 +#+end_example + diff --git a/guides/applications/emacs/use-package.org b/guides/applications/emacs/use-package.org new file mode 100644 index 0000000..f6cd027 --- /dev/null +++ b/guides/applications/emacs/use-package.org @@ -0,0 +1,48 @@ +* Porting from ~custom-file~ +Some very dumb code to generate ~use-package~ declarations from Custom +settings. Entry point is ~c->us/port~. +#+begin_src elisp +(require 'help-fns) +(require 'radix-tree) + +(defun c->us/get-custom-options () + (seq-map + (pcase-lambda (`(theme-value ,option user ,value)) + (list option value)) + (get 'user 'theme-settings))) + +(defun c->us/get-option-file (option) + ;; Load packages first, otherwise symbol-file can return "loaddefs". + (pcase-dolist + (`(_ . ,files) + (radix-tree-prefixes (help-definition-prefixes) + (symbol-name option))) + (dolist (f files) + (load f 'noerror 'nomessage))) + (when-let ((file (symbol-file option))) + (file-name-base file))) + +(defun c->us/write-declaration (lib pairs) + (insert (format "(use-package %s\n" lib)) + (insert " :custom") + (message "%s -> %s" lib pairs) + (pcase-dolist + (`(,option ,value) pairs) + (insert (format "\n (%s %s)" + option + (prin1-to-string value)))) + (insert ")\n\n")) + +(defun c->us/symbols< (symlist1 symlist2) + (string< (car symlist1) (car symlist2))) + +(defun c->us/port () + (seq-map + (pcase-lambda (`(,lib . ,pairs)) + (c->us/write-declaration lib pairs)) + (sort (seq-group-by + (pcase-lambda (`(,option _)) + (c->us/get-option-file option)) + (sort (c->us/get-custom-options) 'c->us/symbols<)) + 'c->us/symbols<))) +#+end_src diff --git a/guides/applications/emacs/windows.org b/guides/applications/emacs/windows.org new file mode 100644 index 0000000..06cad7e --- /dev/null +++ b/guides/applications/emacs/windows.org @@ -0,0 +1,25 @@ +=nt/INSTALL.w64= provides detailed instructions for compiling Emacs on +recent Windows versions. Here are some additional steps I had to +figure out on my own: +* Mingw-w64 +- Set =$HOME= to =%HOMEPATH%= by changing =db_home= to =windows= in + =c:/msys64/etc/fstab=. +- In addition to =c:\msys64\mingw64\bin=, adding =c:\msys64\usr\bin= + to the system =PATH= allows Emacs to access applications installed + with =pacman=. + - It's not obvious whether the Mingw-w64 =PATH= entries should go + last (in which case Emacs will pick the wrong ~find~ command) or + first (in which case… /everything else/ will pick the wrong ~find~ + command?). At least Emacs has a variable to configure + (~find-program~). +* Compiling +Make sure to specify ~-c core.autocrlf=false~ *when cloning* the Emacs +repository; ~autogen.sh~ gets confused otherwise. +* Configuring +AFAICT one must set the variable ~shell-file-name~ explicitly to +=c:/msys64/usr/bin/bash.exe=, otherwise Emacs's Windows-specific +initialization logic uses a built-in shell ersatz (=nt/cmdproxy.c=). +** TODO =HOME= +Went with the instructions from [[info:emacs#MS-Windows Registry]]; could +maybe use [[info:emacs#Windows HOME]] by setting =HOME= to =%HOMEPATH%=, +to avoid having to spell out my username? diff --git a/guides/applications/ffmpeg.org b/guides/applications/ffmpeg.org new file mode 100644 index 0000000..eded0e9 --- /dev/null +++ b/guides/applications/ffmpeg.org @@ -0,0 +1,22 @@ +ffmpeg recipes I should stuff into config files or scripts, but +haven't yet. +* Extracting parts of videos +~-c copy -map 0~ ensures all streams (audio & subtitles) are kept. +The timestamp syntax is described in the "Time duration" section of +=ffmpeg-utils(1)=; =MM:SS= works fine. + +#+begin_src sh +ffmpeg -i $input -c copy -map 0 \ + -ss $tstart -to $tend \ + $output +#+end_src + +* Reduce file size +** with =-vf=: ~-vf "scale=iw/2:ih/2"~ +** with H.265 +openSUSE Tumbleweed's ffmpeg is not built with H.265 support AFAICT. + +1. Install =libx265-devel= +2. Clone =https://git.ffmpeg.org/ffmpeg.git= +3. ~./configure --enable-libx265 --enable-gpl~ +4. ~ffmpeg -i $input -vcodec libx265 -crf 28~ diff --git a/guides/applications/git.org b/guides/applications/git.org new file mode 100644 index 0000000..bf42130 --- /dev/null +++ b/guides/applications/git.org @@ -0,0 +1,40 @@ +* ~send-email~ with ~.authinfo.gpg~ +** Step 1: Install ~git-credential-netrc~ +AFAICT not all distributions package this script? As far as those I +use are concerned: + +- On Debian, the =git= package brings + =/usr/share/doc/git/contrib/credential/netrc/git-credential-netrc.perl=, + but the file is not executable, so it's not as simple as symlinking + it from =~/.local/bin=: it must be =cp='ed and =chmod='ed. + +- openSUSE does not include anything from =contrib=, I think? So just + grab =contrib/credential/netrc/git-credential-netrc.perl= from the [[https://git.kernel.org/pub/scm/git/git.git/][Git + repository]], stick it in =~/.local/bin= and =chmod= it. + +Now ~git help --all | grep credential-~ should show =credential-netrc=. + +** Step 2: Configure ~git send-email~ +In =~/.gitconfig=, assuming a GMail account for =luser@gmail.com=: + +#+begin_src conf +[sendemail] + smtpencryption = tls + smtpserver = smtp.gmail.com + smtpuser = luser + smtpserverport = 587 +#+end_src + +Most =send-email= guides seem to use the full email address for +=smtpuser=; I'm putting in just the /local-part/, since that's what is in +my =~/.authinfo.gpg=. + +** Step 3: Configure ~git-credential~ +In =~/.gitconfig=: + +#+begin_src conf +[credential] + helper = netrc +#+end_src + +** Step 4: Enjoy 📨 diff --git a/guides/applications/imagemagick.org b/guides/applications/imagemagick.org new file mode 100644 index 0000000..9d56cbf --- /dev/null +++ b/guides/applications/imagemagick.org @@ -0,0 +1,81 @@ +ImageMagick recipes I should stuff into config files or scripts, but +haven't yet. +* Configuration +** Allow conversion from/to PDF +- Open ImageMagick's =policy.xml= configuration file. +- Locate the == tag with a =pattern= including "PDF". +- Comment it out. +* Comparing images +~magick compare~ is a thing; ~magick convert~ can also do [[https://stackoverflow.com/a/33673440][neat stuff]]: +#+begin_src sh +convert '(' a.png -flatten -grayscale Rec709Luminance ')' \ + '(' b.png -flatten -grayscale Rec709Luminance ')' \ + '(' -clone 0-1 -compose darken -composite ')' \ + -channel RGB -combine diff.png +#+end_src + +* Adding watermarks +Someday, in the Glorious Future, every thirdparty that insists on +having a copy of my personal documents will be legally obligated to +request it from a trusted government service that steganographizes the +requester's identity in the files they send. + +Until then… +#+begin_src sh +#!/bin/bash + +set -eu + +src=$1 +dst=$2 +label=$3 + +mark () +{ + local marksrc=$1 + local markdst=$2 + + w=$(identify -format '%w\n' "$marksrc" | head -1) + h=$(identify -format '%h\n' "$marksrc" | head -1) + textw=$((w/3)) + texth=$((h/8)) + + pointsize=$((80*w/2500)) + offx=$((100*w/2500)) + offy=$((400*h/3500)) + + convert -size ${textw}x${texth} xc:none \ + -fill '#80808080' -pointsize $pointsize \ + -annotate 340x340+${offx}+${offy} "$label" \ + miff:- | \ + composite -tile - "$marksrc" "$markdst" +} + +if [[ "$src" =~ \.pdf ]] +then + # AFAICT if I feed a multi-page PDF to composite, it outputs only + # the first page. Jump through hoops to split and concatenate the + # PDF; also, roundtrip through JPG because setting -density (to + # preserve a good quality) messes with the size calculations for + # the label. + + tmpd=$(mktemp -d) + bname=$(basename "$src") + qpdf --split-pages "$src" "${tmpd}/$bname" + + shopt -s extglob + + for page in ${tmpd}/${bname/%.pdf/-+([0-9]).pdf} + do + convert -density 300 -flatten "$page" "${page/.pdf/.jpg}" + mark "${page/.pdf/.jpg}" "${page/.pdf/-MARKED.jpg}" + convert -density 300 "${page/.pdf/-MARKED.jpg}" "${page/.pdf/-MARKED.pdf}" + done + + qpdf --empty --pages ${tmpd}/${bname/%.pdf/-+([0-9])-MARKED.pdf} -- "$dst" + + exit +fi + +mark "$src" "$dst" +#+end_src diff --git a/guides/cloud/vps.org b/guides/cloud/vps.org deleted file mode 100644 index 986e373..0000000 --- a/guides/cloud/vps.org +++ /dev/null @@ -1,198 +0,0 @@ -* Security -** Switch APT to HTTPS -~sudo sed -i 's/http:/https:/' /etc/apt/sources.list~ - -Granted, the repository signature provides enough protection; still, -no sense in wasting bandwidth and CPU if someone is meddling. -** Tweak root access -On OVH's Debian image: -- The =root= account has no password. -- =PermitRootLogin= defaults to =prohibit-password=: set it to =no=. -** Enable fail2ban -~lastb~ says there's about 4000 login attempts per day; that makes -=/var/log/btmp= much bigger than it needs to be. - -Debian's fail2ban comes with a jail for ~sshd~, so it's just a matter -of ~apt install fail2ban~. -** Tweak user accounts -=debian= seems mildly popular among bots looking for valid usernames. - -Ideally I'd just rename the =debian= account, but renaming does not -seem to be a very well-defined operation: ~usermod --login $name ---move-home --home /home/$name debian~ gets partway there, but leaves -a bunch of miscellany to take care of (e.g. sudoers). - -So instead, I'll -- create my own user account: ~sudo adduser $name~ -- add it to all groups =debian= belongs to: - #+begin_src sh - groups=$(groups | tr ' ' '\n' | grep -v debian | paste -sd,) - sudo usermod --append --groups ${groups} ${name} - #+end_src -- only allow password authentication over SSH for this new user - account: - #+begin_src conf - PasswordAuthentication no - Match User … - PasswordAuthentication yes - #+end_src - -* System -#+begin_src sh -sudo hostnamectl set-hostname $DOMAIN -sudo timedatectl set-timezone $tz -#+end_src - -* Services -** Web server -Run ~sudo apt install nginx~; then, in -=/etc/nginx/sites-available/$DOMAIN=: -#+begin_src conf -server { - listen 80; - listen [::]:80; - - server_name $DOMAIN www.$DOMAIN; - access_log /var/log/nginx/$DOMAIN.access.log; - - root /var/www/$DOMAIN/html; - index index.html; - - location / { - try_files $uri $uri/ =404; - } -} -#+end_src -Use one =access_log= file per site, to simplify analytics. - -Run ~sudo systemctl restart nginx~. -*** fail2ban -With the following files in =$HOME=: -#+begin_src conf -# nginx-botsearch.local -[Init] - -block = \S*(php|wp-|wordpress|jenkins|hudson|sql|boaform)[^,]* - -[Definition] - -# Change from distro: just remove the leading slash before . -failregex = ^ \- \S+ \[\] \"(GET|POST|HEAD) \S+\" 404 .+$ - ^ \[error\] \d+#\d+: \*\d+ (\S+ )?\"\S+\" (failed|is not found) \(2\: No such file or directory\), client\: \, server\: \S*\, request: \"(GET|POST|HEAD) \/ \S+\"\, .*?$ - -# jail.local -[nginx-http-auth] -enabled = true - -[nginx-botsearch] -enabled = true -# Assume that each requests to $DOMAIN will be logged to "$DOMAIN.access.log". -logpath = /var/log/nginx/*access.log -#+end_src - -Then: -#+begin_src sh -sudo cp ~/nginx-botsearch.local /etc/fail2ban/filter.d/ -sudo cp ~/jail.local /etc/fail2ban/ -sudo systemctl restart fail2ban -#+end_src - -Check how these rules fare against real bot searches with: -#+begin_src sh -fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-botsearch.local -#+end_src - -*** HTTPS -#+begin_src sh -sudo apt install certbot python3-certbot-nginx -sudo certbot --nginx -d $DOMAIN www.$DOMAIN -sudo systemctl reload nginx -#+end_src - -** Git server -*** SSH access -#+begin_src sh -sudo apt install git -sudo tee -a /etc/shells <<< $(which git-shell) -sudo adduser git --disabled-password --shell $(which git-shell) -sudo mkdir /srv/git -sudo chown git:git /srv/git -# For every new repo: -sudo -u git git init --bare --shared=group /srv/git/${repo} -#+end_src - -*** Web mirror -With =/etc/nginx/sites-available/git.$DOMAIN=: -#+begin_src conf -server { - listen 80; - listen [::]:80; - - server_name git.$DOMAIN; - access_log /var/log/nginx/git.$DOMAIN.access.log; - - root /usr/share/cgit; - try_files $uri @cgit; - - location @cgit { - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi; - fastcgi_param PATH_INFO $uri; - fastcgi_param QUERY_STRING $args; - fastcgi_param HTTP_HOST $server_name; - fastcgi_pass unix:/run/fcgiwrap.socket; - } -} -#+end_src - -And =/etc/cgitrc/=: -#+begin_src conf -css=/cgit.css -logo=/cgit.png - -virtual-root=/ -# Change to https:// after setting up certbot: -clone-url=http://git.$DOMAIN/$CGIT_REPO_URL -snapshots=tar.xz - -enable-git-config=1 -enable-http-clone=1 -enable-index-owner=0 - -scan-path=/srv/git -#+end_src - -In each repository: -- fill in =description=, -- fill =[cgit]= section in =config= (=hide=, =owner=, =section=). - -Do: -#+begin_src sh -sudo apt install cgit fcgiwrap -( cd /etc/sites-enabled/ && ln -s ../sites-avaiable/git.$DOMAIN . ) -sudo systemctl restart nginx -# Make fail2ban notice the new log file. -sudo systemctl restart fail2ban -#+end_src - -**** "Idle" vs default branch -cgit struggles to guess what to print for the "Idle" column on the -index page when the default branch is not "master". [[https://lists.zx2c4.com/pipermail/cgit/2020-August/004515.html][Workarounds]]: - -- set =repo.defbranch=, -- update =agefile= with [[https://git.zx2c4.com/cgit/tree/contrib/hooks/post-receive.agefile][a post-receive hook]]. -** CGI -I like the idea of [[https://en.wikipedia.org/wiki/Common_Gateway_Interface#Using_CGI_scripts][CGI "scripts"]], i.e. having the web server fire a -program to handle requests: - -- URI bits are passed through environment variables and stdin; -- the program spits the page on stdout. - -Had some fun toying with Python's ~cgi~ module; sadly though the -project has [[https://peps.python.org/pep-0594/#cgi][decided to deprecate it]]. The docs [[https://docs.python.org/3.11/library/cgi.html][suggest some migration -paths]], and there's a [[https://pypi.org/project/legacy-cgi/][legacy-cgi package on PyPI]] if I really want to -keep using it I guess. - -Also nginx has no support for CGI either, though their documentation -explains how to combine their FastCGI support with ~fcgiwrap~ to -enable CGI scripts. diff --git a/guides/communication/jargon.org b/guides/communication/jargon.org new file mode 100644 index 0000000..544929f --- /dev/null +++ b/guides/communication/jargon.org @@ -0,0 +1,13 @@ +Things I say that come from obscure recesses of my brain, and are +therefore unlikely to be self-explanatory. + +* Pseudolatin +Because it makes me chortle. + +- reductio ad :: + accusative; variants on /reductio ad absurdum/ I + use to signify why I find some line of reasoning unconvincing. + + - canitiem, acerbum usum :: "gray hair", "bitter experience". I + seek and value the wisdom of those who came before me; however I + would much rather they distill this wisdom into actionable + principles I can augment my own judgment with. diff --git a/guides/communication/writing.md b/guides/communication/writing.md new file mode 100644 index 0000000..4231353 --- /dev/null +++ b/guides/communication/writing.md @@ -0,0 +1,45 @@ +I worry a lot about how efficient my writing is. I want to keep it as +straightforward (maximizing signal-to-noise ratio) and accurate +(citing sources and assumptions) as I can. + +Since I keep catching myself making the same mistakes, and I do not +have automated tools to warn me about them yet, I figured I should +write down all the things I want to watch out for. + +# Decruftification + +## Sentences + +- Watch out for verbs which hide the action (e.g. try, allow). +- Use simpler words as long as they are not more vague. + +## Mails + +- Pick your battles: people only have time for so many subjects. + Focus on symptomatic relief; hint at systemic problems but wait + until prompted before ranting. +- Tricks to make mails more digestible: + - condense the point(s) into a tl;dr; + - move details (investigation, alternatives, rationale) into + footnotes or attachments; + - spin new threads. + +# Logic + +When expressing causation, check that all causes have been explicitly +stated. Be wary of assumptions^[I felt like adding "protect yourself +against misquotations", but those will probably happen anyway]. + +Conversely, if C can be deduced from A alone, consider rewriting +"since A and B, C" into "since A, C". Unnecessary Bs can be +detrimental to the discussion and make it to go off-topic; beware: + +- humorous overstatements that might be taken seriously; +- subconscious attempts at shoehorning a point that feels essential + but is not actually relevant. + +# Resources + +- [IEEE's "Write Clearly and Concisely"](http://sites.ieee.org/pcs/communication-resources-for-engineers/style/write-clearly-and-concisely/) +- [Gitlab's Technical Writing Handbook](https://about.gitlab.com/handbook/product/technical-writing/) +- [Lessons from my PhD — Austin Z. Henley](https://austinhenley.com/blog/lessonsfrommyphd.html) diff --git a/guides/emacs/AUCTeX.org b/guides/emacs/AUCTeX.org deleted file mode 100644 index 0082759..0000000 --- a/guides/emacs/AUCTeX.org +++ /dev/null @@ -1,24 +0,0 @@ -* Completion for cross-references -To integrate AUCTeX with reftex and get completion for ~\cite~ -(bibliography) and ~\ref~ (intra-document labels): -#+begin_src elisp -;; In init file: -(put 'TeX-auto-save 'safe-local-variable 'booleanp) -(put 'TeX-parse-self 'safe-local-variable 'booleanp) -(add-hook 'LaTeX-mode-hook 'turn-on-reftex) -(setq reftex-plug-into-AUCTeX t) -(setq reftex-label-illegal-re "[^-a-zA-Z0-9_+=:;,./]") ; Allow slashes. -(setq TeX-auto-local ".auctex") - -;; Top-level .dir-locals.el: -((latex-mode - . ((TeX-auto-save . t) - (TeX-parse-self . t))) - (bibtex-mode - . ((TeX-auto-save . t) - (TeX-parse-self . t)))) - -;; .dir-locals.el files in subfolders: -((latex-mode - . ((TeX-master . "../path/to/main.tex")))) -#+end_src diff --git a/guides/emacs/development.org b/guides/emacs/development.org deleted file mode 100644 index f1363e1..0000000 --- a/guides/emacs/development.org +++ /dev/null @@ -1,121 +0,0 @@ -* Build -** bootstrapping -Libraries that are part of the "bootstrap set" cannot rely on -autoloading. Example: =files.el= must ~(eval-when-compile (require -'pcase))~, even though it only uses autoloaded macros. -* Tests -** Running -Somehow I can never remember how to ask =make= how to run a specific -set of tests. - -tl;dr, running =$testcases_regexp= from =test/$testfile.el=, to test -changes in =lisp/$sourcefile.el=: -#+begin_src sh -make -C test $testfile \ - TEST_LOAD_EL=yes \ - TEST_BACKTRACE_LINE_LENGTH=nil \ - SELECTOR="\"$testcases_regexp\"" \ - EMACS_EXTRAOPT="-l ../lisp/$sourcefile.el" -#+end_src - -Relevant bits from test/Makefile: - -#+begin_src makefile -## filename.log: run tests from filename.el(c) if .log file needs updating -## filename: re-run tests from filename.el(c), with no logging -#+end_src - -#+begin_src makefile -# Whether to run tests from .el files in preference to .elc, we do -# this by default since it gives nicer stacktraces. -# If you just want a pass/fail, setting this to no is much faster. -export TEST_LOAD_EL ?= \ - $(if $(findstring $(MAKECMDGOALS), all check check-maybe),no,yes) -#+end_src - -#+begin_src makefile -TEST_RUN_ERT = --batch --eval '(ert-run-tests-batch-and-exit (quote ${SELECTOR_ACTUAL}))' ${WRITE_LOG} -#+end_src - -#+begin_src makefile -ifdef SELECTOR -SELECTOR_ACTUAL=$(SELECTOR) -#+end_src - -Selector documentation from ~ert-select-tests~: -#+begin_quote -Valid SELECTORs: - -nil -- Selects the empty set. -t -- Selects UNIVERSE. -:new -- Selects all tests that have not been run yet. -:failed, :passed -- Select tests according to their most recent result. -:expected, :unexpected -- Select tests according to their most recent result. -a string -- A regular expression selecting all tests with matching names. -a test -- (i.e., an object of the ert-test data-type) Selects that test. -a symbol -- Selects the test that the symbol names, signals an - ‘ert-test-unbound’ error if none. -(member TESTS...) -- Selects the elements of TESTS, a list of tests - or symbols naming tests. -(eql TEST) -- Selects TEST, a test or a symbol naming a test. -(and SELECTORS...) -- Selects the tests that match all SELECTORS. -(or SELECTORS...) -- Selects the tests that match any of the SELECTORS. -(not SELECTOR) -- Selects all tests that do not match SELECTOR. -(tag TAG) -- Selects all tests that have TAG on their tags list. - A tag is an arbitrary label you can apply when you define a test. -(satisfies PREDICATE) -- Selects all tests that satisfy PREDICATE. - PREDICATE is a function that takes an ert-test object as argument, - and returns non-nil if it is selected. -#+end_quote -** Writing -*** Mocking functions -#+begin_src elisp -(cl-letf (((symbol-function 'read-something) - (lambda (&rest _) "result"))) - (some-command)) -#+end_src - -* Third-party packages -** forge -*** build -Use =config.mk= to set =LOAD_PATH=: -#+begin_src makefile -find-lib = $(dir $(shell emacs -Q -batch \ - -eval "(require 'find-func)" \ - -eval "(package-initialize)" \ - -eval "(require '$(1))" \ - -eval "(princ (find-library-name \"$(1)\"))")) - -sources = ~/src/emacs/magit/lisp - -elpa = \ - closql \ - compat \ - dash \ - emacsql \ - ghub \ - markdown-mode \ - transient \ - treepy \ - with-editor \ - yaml -elpa_dirs = $(foreach d,$(elpa),$(call find-lib,$(d))) - -LOAD_PATH = $(addprefix -L ,$(elpa_dirs) $(sources)) -#+end_src - -*** load sources -~emacs -Q -L ./lisp~ does not seem to work: after running -~(package-initialize)~, =M-x find-library forge= returns the instance -installed under ~package-user-dir~. - -This can be circumvented with ~(setq pacakge-load-list '((forge nil) -all))~. - -Full command line for easy startup: - -#+begin_src sh -emacs -Q -L ./lisp \ - -eval "(setq pacakge-load-list '((forge nil) all))" \ - -f package-initialize \ -#+end_src diff --git a/guides/emacs/elisp-demos.org b/guides/emacs/elisp-demos.org deleted file mode 100644 index 029f5be..0000000 --- a/guides/emacs/elisp-demos.org +++ /dev/null @@ -1,28 +0,0 @@ -Some terse "context-free" Emacs Lisp snippets that hopefully speak for -themselves. - -* The prefix argument -#+begin_src elisp -(defun my/prefix-arg-demo (arg) - (interactive (list current-prefix-arg)) - (insert - (let ((num (prefix-numeric-value arg))) - (format "%s %d %s\n" - arg - num - (cond ((null arg) "N/A") - ((numberp arg) (format "M-%d or C-u %d" arg arg)) - ((equal arg '-) (format "M-- or C-u -")) - ((listp arg) - (if (< num 0) - (format "(M-- or C-u -) C-u × %d" (log (- num) 4)) - (format "C-u × %d" (log num 4))))))))) -#+end_src -#+begin_example -nil 1 N/A -2 2 M-2 or C-u 2 -- -1 M-- or C-u - -(-16) -16 (M-- or C-u -) C-u × 2 -(64) 64 C-u × 3 -#+end_example - diff --git a/guides/emacs/use-package.org b/guides/emacs/use-package.org deleted file mode 100644 index f6cd027..0000000 --- a/guides/emacs/use-package.org +++ /dev/null @@ -1,48 +0,0 @@ -* Porting from ~custom-file~ -Some very dumb code to generate ~use-package~ declarations from Custom -settings. Entry point is ~c->us/port~. -#+begin_src elisp -(require 'help-fns) -(require 'radix-tree) - -(defun c->us/get-custom-options () - (seq-map - (pcase-lambda (`(theme-value ,option user ,value)) - (list option value)) - (get 'user 'theme-settings))) - -(defun c->us/get-option-file (option) - ;; Load packages first, otherwise symbol-file can return "loaddefs". - (pcase-dolist - (`(_ . ,files) - (radix-tree-prefixes (help-definition-prefixes) - (symbol-name option))) - (dolist (f files) - (load f 'noerror 'nomessage))) - (when-let ((file (symbol-file option))) - (file-name-base file))) - -(defun c->us/write-declaration (lib pairs) - (insert (format "(use-package %s\n" lib)) - (insert " :custom") - (message "%s -> %s" lib pairs) - (pcase-dolist - (`(,option ,value) pairs) - (insert (format "\n (%s %s)" - option - (prin1-to-string value)))) - (insert ")\n\n")) - -(defun c->us/symbols< (symlist1 symlist2) - (string< (car symlist1) (car symlist2))) - -(defun c->us/port () - (seq-map - (pcase-lambda (`(,lib . ,pairs)) - (c->us/write-declaration lib pairs)) - (sort (seq-group-by - (pcase-lambda (`(,option _)) - (c->us/get-option-file option)) - (sort (c->us/get-custom-options) 'c->us/symbols<)) - 'c->us/symbols<))) -#+end_src diff --git a/guides/emacs/windows.org b/guides/emacs/windows.org deleted file mode 100644 index 06cad7e..0000000 --- a/guides/emacs/windows.org +++ /dev/null @@ -1,25 +0,0 @@ -=nt/INSTALL.w64= provides detailed instructions for compiling Emacs on -recent Windows versions. Here are some additional steps I had to -figure out on my own: -* Mingw-w64 -- Set =$HOME= to =%HOMEPATH%= by changing =db_home= to =windows= in - =c:/msys64/etc/fstab=. -- In addition to =c:\msys64\mingw64\bin=, adding =c:\msys64\usr\bin= - to the system =PATH= allows Emacs to access applications installed - with =pacman=. - - It's not obvious whether the Mingw-w64 =PATH= entries should go - last (in which case Emacs will pick the wrong ~find~ command) or - first (in which case… /everything else/ will pick the wrong ~find~ - command?). At least Emacs has a variable to configure - (~find-program~). -* Compiling -Make sure to specify ~-c core.autocrlf=false~ *when cloning* the Emacs -repository; ~autogen.sh~ gets confused otherwise. -* Configuring -AFAICT one must set the variable ~shell-file-name~ explicitly to -=c:/msys64/usr/bin/bash.exe=, otherwise Emacs's Windows-specific -initialization logic uses a built-in shell ersatz (=nt/cmdproxy.c=). -** TODO =HOME= -Went with the instructions from [[info:emacs#MS-Windows Registry]]; could -maybe use [[info:emacs#Windows HOME]] by setting =HOME= to =%HOMEPATH%=, -to avoid having to spell out my username? diff --git a/guides/ffmpeg.org b/guides/ffmpeg.org deleted file mode 100644 index eded0e9..0000000 --- a/guides/ffmpeg.org +++ /dev/null @@ -1,22 +0,0 @@ -ffmpeg recipes I should stuff into config files or scripts, but -haven't yet. -* Extracting parts of videos -~-c copy -map 0~ ensures all streams (audio & subtitles) are kept. -The timestamp syntax is described in the "Time duration" section of -=ffmpeg-utils(1)=; =MM:SS= works fine. - -#+begin_src sh -ffmpeg -i $input -c copy -map 0 \ - -ss $tstart -to $tend \ - $output -#+end_src - -* Reduce file size -** with =-vf=: ~-vf "scale=iw/2:ih/2"~ -** with H.265 -openSUSE Tumbleweed's ffmpeg is not built with H.265 support AFAICT. - -1. Install =libx265-devel= -2. Clone =https://git.ffmpeg.org/ffmpeg.git= -3. ~./configure --enable-libx265 --enable-gpl~ -4. ~ffmpeg -i $input -vcodec libx265 -crf 28~ diff --git a/guides/git.org b/guides/git.org deleted file mode 100644 index bf42130..0000000 --- a/guides/git.org +++ /dev/null @@ -1,40 +0,0 @@ -* ~send-email~ with ~.authinfo.gpg~ -** Step 1: Install ~git-credential-netrc~ -AFAICT not all distributions package this script? As far as those I -use are concerned: - -- On Debian, the =git= package brings - =/usr/share/doc/git/contrib/credential/netrc/git-credential-netrc.perl=, - but the file is not executable, so it's not as simple as symlinking - it from =~/.local/bin=: it must be =cp='ed and =chmod='ed. - -- openSUSE does not include anything from =contrib=, I think? So just - grab =contrib/credential/netrc/git-credential-netrc.perl= from the [[https://git.kernel.org/pub/scm/git/git.git/][Git - repository]], stick it in =~/.local/bin= and =chmod= it. - -Now ~git help --all | grep credential-~ should show =credential-netrc=. - -** Step 2: Configure ~git send-email~ -In =~/.gitconfig=, assuming a GMail account for =luser@gmail.com=: - -#+begin_src conf -[sendemail] - smtpencryption = tls - smtpserver = smtp.gmail.com - smtpuser = luser - smtpserverport = 587 -#+end_src - -Most =send-email= guides seem to use the full email address for -=smtpuser=; I'm putting in just the /local-part/, since that's what is in -my =~/.authinfo.gpg=. - -** Step 3: Configure ~git-credential~ -In =~/.gitconfig=: - -#+begin_src conf -[credential] - helper = netrc -#+end_src - -** Step 4: Enjoy 📨 diff --git a/guides/imagemagick.org b/guides/imagemagick.org deleted file mode 100644 index 9d56cbf..0000000 --- a/guides/imagemagick.org +++ /dev/null @@ -1,81 +0,0 @@ -ImageMagick recipes I should stuff into config files or scripts, but -haven't yet. -* Configuration -** Allow conversion from/to PDF -- Open ImageMagick's =policy.xml= configuration file. -- Locate the == tag with a =pattern= including "PDF". -- Comment it out. -* Comparing images -~magick compare~ is a thing; ~magick convert~ can also do [[https://stackoverflow.com/a/33673440][neat stuff]]: -#+begin_src sh -convert '(' a.png -flatten -grayscale Rec709Luminance ')' \ - '(' b.png -flatten -grayscale Rec709Luminance ')' \ - '(' -clone 0-1 -compose darken -composite ')' \ - -channel RGB -combine diff.png -#+end_src - -* Adding watermarks -Someday, in the Glorious Future, every thirdparty that insists on -having a copy of my personal documents will be legally obligated to -request it from a trusted government service that steganographizes the -requester's identity in the files they send. - -Until then… -#+begin_src sh -#!/bin/bash - -set -eu - -src=$1 -dst=$2 -label=$3 - -mark () -{ - local marksrc=$1 - local markdst=$2 - - w=$(identify -format '%w\n' "$marksrc" | head -1) - h=$(identify -format '%h\n' "$marksrc" | head -1) - textw=$((w/3)) - texth=$((h/8)) - - pointsize=$((80*w/2500)) - offx=$((100*w/2500)) - offy=$((400*h/3500)) - - convert -size ${textw}x${texth} xc:none \ - -fill '#80808080' -pointsize $pointsize \ - -annotate 340x340+${offx}+${offy} "$label" \ - miff:- | \ - composite -tile - "$marksrc" "$markdst" -} - -if [[ "$src" =~ \.pdf ]] -then - # AFAICT if I feed a multi-page PDF to composite, it outputs only - # the first page. Jump through hoops to split and concatenate the - # PDF; also, roundtrip through JPG because setting -density (to - # preserve a good quality) messes with the size calculations for - # the label. - - tmpd=$(mktemp -d) - bname=$(basename "$src") - qpdf --split-pages "$src" "${tmpd}/$bname" - - shopt -s extglob - - for page in ${tmpd}/${bname/%.pdf/-+([0-9]).pdf} - do - convert -density 300 -flatten "$page" "${page/.pdf/.jpg}" - mark "${page/.pdf/.jpg}" "${page/.pdf/-MARKED.jpg}" - convert -density 300 "${page/.pdf/-MARKED.jpg}" "${page/.pdf/-MARKED.pdf}" - done - - qpdf --empty --pages ${tmpd}/${bname/%.pdf/-+([0-9])-MARKED.pdf} -- "$dst" - - exit -fi - -mark "$src" "$dst" -#+end_src diff --git a/guides/jargon.org b/guides/jargon.org deleted file mode 100644 index 544929f..0000000 --- a/guides/jargon.org +++ /dev/null @@ -1,13 +0,0 @@ -Things I say that come from obscure recesses of my brain, and are -therefore unlikely to be self-explanatory. - -* Pseudolatin -Because it makes me chortle. - -- reductio ad :: + accusative; variants on /reductio ad absurdum/ I - use to signify why I find some line of reasoning unconvincing. - - - canitiem, acerbum usum :: "gray hair", "bitter experience". I - seek and value the wisdom of those who came before me; however I - would much rather they distill this wisdom into actionable - principles I can augment my own judgment with. diff --git a/guides/languages/python.org b/guides/languages/python.org new file mode 100644 index 0000000..3c11b13 --- /dev/null +++ b/guides/languages/python.org @@ -0,0 +1,40 @@ +* IDE setup +#+begin_src sh +pip install --user 'python-lsp-server[all]' +pip install --user pylsp-mypy +#+end_src + +This seems to be enough for Eglot to turn Emacs into a Python IDE. +This recipe will probably break once PEP 668 hits my distros; I guess +I'll switch to pipx at that stage? + +Tried to get the LSP server packaged by my distro, but openSUSE +Tumbleweed at least does not make it easy: + +- Python packages are explicitly named =python3$y-…=, so asking zypper + install those means setting myself up for suffering when Tumbleweed + moves to version =$((++y))= and I need to chase down everything I've + manually installed to (a) install the newer versions (b) clean up + every remnant of the previous versions & the corresponding Python + runtime. + +- I have no idea how to cajole zypper into installing those + "suggested" dependencies; =--recommended= does not cut it, + =--suggested= does not exist. + + #+begin_src + $ sudo zypper install python310-python-lsp-server + Loading repository data... + Reading installed packages... + Resolving package dependencies... + + The following 10 packages are suggested, but will not be installed: + python310-autopep8 python310-flake8 python310-mccabe python310-pycodestyle python310-pydocstyle + python310-pyflakes python310-pylint python310-rope python310-whatthepatch python310-yapf + + The following 4 NEW packages are going to be installed: + python310-docstring-to-markdown python310-python-lsp-jsonrpc python310-python-lsp-server python310-ujson + + 4 new packages to install. + #+end_src + diff --git a/guides/languages/rust/error-handling.org b/guides/languages/rust/error-handling.org new file mode 100644 index 0000000..d56ab11 --- /dev/null +++ b/guides/languages/rust/error-handling.org @@ -0,0 +1,16 @@ +* Crates +Error-handling crates seem to rise and fall pretty regularly, each new +crate leveraging new primitives in the standard library to push +ergonomics even further: + +- error_chain +- failure +- snafu +- thiserror +- anyhow +- eyre +* Documentation +- Error handling now has a [[https://github.com/rust-lang/project-error-handling][dedicated project group]] under the [[https://www.rust-lang.org/governance/teams/library][library + team]]. +- [[https://nrc.github.io/error-docs/intro.html][Nick Cameron's error-handling guide]] (2022) +- [[https://www.howtocodeit.com/articles/the-definitive-guide-to-rust-error-handling][Angus Morrison's "Definitive Guide"]] (2024) diff --git a/guides/languages/shell-101.org b/guides/languages/shell-101.org new file mode 100644 index 0000000..2131faa --- /dev/null +++ b/guides/languages/shell-101.org @@ -0,0 +1,44 @@ +My tentative introductory guide to navigating a shell interface +(assuming Bash). + +🚧 These are rough notes meant for guiding in-person discussions; they +are somewhat terse. 🚧 + +* Moving around, invoking stuff + +With a file browser and a terminal emulator side-by-side: + +- ls, cd, mkdir, touch, rm; "/" = root +- globs +- arguments vs options (e.g. ls, diff) +- man, --help +- standard input/output/error: pipes, redirection +- writing scripts (shebangs: see bash(1) § COMMAND EXECUTION) + +* Shell language + +- ; & (e.g. sleep) +- && || (e.g. grep) +- if, for +- $? (e.g. grep, diff) +- [ +- variables +- functions, $@, $1… +- black-box model of a command: + - inputs: stdin, arguments, environment + - outputs: stdout, stderr, exit code + - side-effects: I/O, e.g. files, network +- which, type: keyword ≠ builtin ≠ function ≠ program +- valar quotulis (valar escaepis); e.g. sort operator: [ foo '>' bar ] + +* Advanced + +- ( ) ≠ { }: try to set variables, or call exit +- (( )), $( ) +- glob ≠ regex +- bash extensions + - [[ + - <( ) + - |&, >& + - arrays +- job control: fg, bg, jobs, wait, kill, C-z diff --git a/guides/mail.org b/guides/mail.org deleted file mode 100644 index 014e40f..0000000 --- a/guides/mail.org +++ /dev/null @@ -1,27 +0,0 @@ -* Providers -| provider | base | custom domain | price (/y) | ¤ | storage (GB) | max in (mail/d) | max out (mail/d) | -|--------------------+------+---------------+------------+---+--------------+-----------------+------------------| -| fastmail (basic) | 🇦🇺 | n | 30 | $ | 2 | 4000 | 4000 | -| mailbox (standard) | 🇩🇪 | y | 36 | € | 10 | | 1000 | -| migadu (micro) | 🇨🇭 | y | 19 | $ | 5 | 200 | 20 | -| posteo | 🇩🇪 | n | 12 | € | 2 | ∞ | ∞ | -| zaclys | 🇫🇷 | n | 10 | € | 10 | | | - -- FastMail - - https://www.fastmail.com/pricing/ - - [[https://www.fastmail.help/hc/en-us/articles/1500000277382-Account-limits][receiving & sending limits]] - - custom domain available at "standard" plan -- mailbox.org - - https://mailbox.org/en/services#price-plans - - [[https://kb.mailbox.org/service-desk/e-mail-article/is-there-a-limit-on-the-amount-of-e-mails-that-i-can-send-from-my-account][sending limit]] -- Migadu - - https://www.migadu.com/pricing/ -- Posteo - - https://posteo.de/en/site/features - - [[https://posteo.de/en/help/what-restrictions-are-there-on-new-unpaid-email-accounts][receiving & sending limits, or lack thereof:]] "Once we have - received your payment, all restrictions will be removed." -- Zaclys - - https://www.zaclys.com/souscrire/ - - ≤100 recipients/mail - - +1GB/€ - - comes with cloud diff --git a/guides/python.org b/guides/python.org deleted file mode 100644 index 3c11b13..0000000 --- a/guides/python.org +++ /dev/null @@ -1,40 +0,0 @@ -* IDE setup -#+begin_src sh -pip install --user 'python-lsp-server[all]' -pip install --user pylsp-mypy -#+end_src - -This seems to be enough for Eglot to turn Emacs into a Python IDE. -This recipe will probably break once PEP 668 hits my distros; I guess -I'll switch to pipx at that stage? - -Tried to get the LSP server packaged by my distro, but openSUSE -Tumbleweed at least does not make it easy: - -- Python packages are explicitly named =python3$y-…=, so asking zypper - install those means setting myself up for suffering when Tumbleweed - moves to version =$((++y))= and I need to chase down everything I've - manually installed to (a) install the newer versions (b) clean up - every remnant of the previous versions & the corresponding Python - runtime. - -- I have no idea how to cajole zypper into installing those - "suggested" dependencies; =--recommended= does not cut it, - =--suggested= does not exist. - - #+begin_src - $ sudo zypper install python310-python-lsp-server - Loading repository data... - Reading installed packages... - Resolving package dependencies... - - The following 10 packages are suggested, but will not be installed: - python310-autopep8 python310-flake8 python310-mccabe python310-pycodestyle python310-pydocstyle - python310-pyflakes python310-pylint python310-rope python310-whatthepatch python310-yapf - - The following 4 NEW packages are going to be installed: - python310-docstring-to-markdown python310-python-lsp-jsonrpc python310-python-lsp-server python310-ujson - - 4 new packages to install. - #+end_src - diff --git a/guides/rust/error-handling.org b/guides/rust/error-handling.org deleted file mode 100644 index d56ab11..0000000 --- a/guides/rust/error-handling.org +++ /dev/null @@ -1,16 +0,0 @@ -* Crates -Error-handling crates seem to rise and fall pretty regularly, each new -crate leveraging new primitives in the standard library to push -ergonomics even further: - -- error_chain -- failure -- snafu -- thiserror -- anyhow -- eyre -* Documentation -- Error handling now has a [[https://github.com/rust-lang/project-error-handling][dedicated project group]] under the [[https://www.rust-lang.org/governance/teams/library][library - team]]. -- [[https://nrc.github.io/error-docs/intro.html][Nick Cameron's error-handling guide]] (2022) -- [[https://www.howtocodeit.com/articles/the-definitive-guide-to-rust-error-handling][Angus Morrison's "Definitive Guide"]] (2024) diff --git a/guides/services/mail.org b/guides/services/mail.org new file mode 100644 index 0000000..014e40f --- /dev/null +++ b/guides/services/mail.org @@ -0,0 +1,27 @@ +* Providers +| provider | base | custom domain | price (/y) | ¤ | storage (GB) | max in (mail/d) | max out (mail/d) | +|--------------------+------+---------------+------------+---+--------------+-----------------+------------------| +| fastmail (basic) | 🇦🇺 | n | 30 | $ | 2 | 4000 | 4000 | +| mailbox (standard) | 🇩🇪 | y | 36 | € | 10 | | 1000 | +| migadu (micro) | 🇨🇭 | y | 19 | $ | 5 | 200 | 20 | +| posteo | 🇩🇪 | n | 12 | € | 2 | ∞ | ∞ | +| zaclys | 🇫🇷 | n | 10 | € | 10 | | | + +- FastMail + - https://www.fastmail.com/pricing/ + - [[https://www.fastmail.help/hc/en-us/articles/1500000277382-Account-limits][receiving & sending limits]] + - custom domain available at "standard" plan +- mailbox.org + - https://mailbox.org/en/services#price-plans + - [[https://kb.mailbox.org/service-desk/e-mail-article/is-there-a-limit-on-the-amount-of-e-mails-that-i-can-send-from-my-account][sending limit]] +- Migadu + - https://www.migadu.com/pricing/ +- Posteo + - https://posteo.de/en/site/features + - [[https://posteo.de/en/help/what-restrictions-are-there-on-new-unpaid-email-accounts][receiving & sending limits, or lack thereof:]] "Once we have + received your payment, all restrictions will be removed." +- Zaclys + - https://www.zaclys.com/souscrire/ + - ≤100 recipients/mail + - +1GB/€ + - comes with cloud diff --git a/guides/shell-101.org b/guides/shell-101.org deleted file mode 100644 index 2131faa..0000000 --- a/guides/shell-101.org +++ /dev/null @@ -1,44 +0,0 @@ -My tentative introductory guide to navigating a shell interface -(assuming Bash). - -🚧 These are rough notes meant for guiding in-person discussions; they -are somewhat terse. 🚧 - -* Moving around, invoking stuff - -With a file browser and a terminal emulator side-by-side: - -- ls, cd, mkdir, touch, rm; "/" = root -- globs -- arguments vs options (e.g. ls, diff) -- man, --help -- standard input/output/error: pipes, redirection -- writing scripts (shebangs: see bash(1) § COMMAND EXECUTION) - -* Shell language - -- ; & (e.g. sleep) -- && || (e.g. grep) -- if, for -- $? (e.g. grep, diff) -- [ -- variables -- functions, $@, $1… -- black-box model of a command: - - inputs: stdin, arguments, environment - - outputs: stdout, stderr, exit code - - side-effects: I/O, e.g. files, network -- which, type: keyword ≠ builtin ≠ function ≠ program -- valar quotulis (valar escaepis); e.g. sort operator: [ foo '>' bar ] - -* Advanced - -- ( ) ≠ { }: try to set variables, or call exit -- (( )), $( ) -- glob ≠ regex -- bash extensions - - [[ - - <( ) - - |&, >& - - arrays -- job control: fg, bg, jobs, wait, kill, C-z diff --git a/guides/sysadmin/cloud/vps.org b/guides/sysadmin/cloud/vps.org new file mode 100644 index 0000000..986e373 --- /dev/null +++ b/guides/sysadmin/cloud/vps.org @@ -0,0 +1,198 @@ +* Security +** Switch APT to HTTPS +~sudo sed -i 's/http:/https:/' /etc/apt/sources.list~ + +Granted, the repository signature provides enough protection; still, +no sense in wasting bandwidth and CPU if someone is meddling. +** Tweak root access +On OVH's Debian image: +- The =root= account has no password. +- =PermitRootLogin= defaults to =prohibit-password=: set it to =no=. +** Enable fail2ban +~lastb~ says there's about 4000 login attempts per day; that makes +=/var/log/btmp= much bigger than it needs to be. + +Debian's fail2ban comes with a jail for ~sshd~, so it's just a matter +of ~apt install fail2ban~. +** Tweak user accounts +=debian= seems mildly popular among bots looking for valid usernames. + +Ideally I'd just rename the =debian= account, but renaming does not +seem to be a very well-defined operation: ~usermod --login $name +--move-home --home /home/$name debian~ gets partway there, but leaves +a bunch of miscellany to take care of (e.g. sudoers). + +So instead, I'll +- create my own user account: ~sudo adduser $name~ +- add it to all groups =debian= belongs to: + #+begin_src sh + groups=$(groups | tr ' ' '\n' | grep -v debian | paste -sd,) + sudo usermod --append --groups ${groups} ${name} + #+end_src +- only allow password authentication over SSH for this new user + account: + #+begin_src conf + PasswordAuthentication no + Match User … + PasswordAuthentication yes + #+end_src + +* System +#+begin_src sh +sudo hostnamectl set-hostname $DOMAIN +sudo timedatectl set-timezone $tz +#+end_src + +* Services +** Web server +Run ~sudo apt install nginx~; then, in +=/etc/nginx/sites-available/$DOMAIN=: +#+begin_src conf +server { + listen 80; + listen [::]:80; + + server_name $DOMAIN www.$DOMAIN; + access_log /var/log/nginx/$DOMAIN.access.log; + + root /var/www/$DOMAIN/html; + index index.html; + + location / { + try_files $uri $uri/ =404; + } +} +#+end_src +Use one =access_log= file per site, to simplify analytics. + +Run ~sudo systemctl restart nginx~. +*** fail2ban +With the following files in =$HOME=: +#+begin_src conf +# nginx-botsearch.local +[Init] + +block = \S*(php|wp-|wordpress|jenkins|hudson|sql|boaform)[^,]* + +[Definition] + +# Change from distro: just remove the leading slash before . +failregex = ^ \- \S+ \[\] \"(GET|POST|HEAD) \S+\" 404 .+$ + ^ \[error\] \d+#\d+: \*\d+ (\S+ )?\"\S+\" (failed|is not found) \(2\: No such file or directory\), client\: \, server\: \S*\, request: \"(GET|POST|HEAD) \/ \S+\"\, .*?$ + +# jail.local +[nginx-http-auth] +enabled = true + +[nginx-botsearch] +enabled = true +# Assume that each requests to $DOMAIN will be logged to "$DOMAIN.access.log". +logpath = /var/log/nginx/*access.log +#+end_src + +Then: +#+begin_src sh +sudo cp ~/nginx-botsearch.local /etc/fail2ban/filter.d/ +sudo cp ~/jail.local /etc/fail2ban/ +sudo systemctl restart fail2ban +#+end_src + +Check how these rules fare against real bot searches with: +#+begin_src sh +fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-botsearch.local +#+end_src + +*** HTTPS +#+begin_src sh +sudo apt install certbot python3-certbot-nginx +sudo certbot --nginx -d $DOMAIN www.$DOMAIN +sudo systemctl reload nginx +#+end_src + +** Git server +*** SSH access +#+begin_src sh +sudo apt install git +sudo tee -a /etc/shells <<< $(which git-shell) +sudo adduser git --disabled-password --shell $(which git-shell) +sudo mkdir /srv/git +sudo chown git:git /srv/git +# For every new repo: +sudo -u git git init --bare --shared=group /srv/git/${repo} +#+end_src + +*** Web mirror +With =/etc/nginx/sites-available/git.$DOMAIN=: +#+begin_src conf +server { + listen 80; + listen [::]:80; + + server_name git.$DOMAIN; + access_log /var/log/nginx/git.$DOMAIN.access.log; + + root /usr/share/cgit; + try_files $uri @cgit; + + location @cgit { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi; + fastcgi_param PATH_INFO $uri; + fastcgi_param QUERY_STRING $args; + fastcgi_param HTTP_HOST $server_name; + fastcgi_pass unix:/run/fcgiwrap.socket; + } +} +#+end_src + +And =/etc/cgitrc/=: +#+begin_src conf +css=/cgit.css +logo=/cgit.png + +virtual-root=/ +# Change to https:// after setting up certbot: +clone-url=http://git.$DOMAIN/$CGIT_REPO_URL +snapshots=tar.xz + +enable-git-config=1 +enable-http-clone=1 +enable-index-owner=0 + +scan-path=/srv/git +#+end_src + +In each repository: +- fill in =description=, +- fill =[cgit]= section in =config= (=hide=, =owner=, =section=). + +Do: +#+begin_src sh +sudo apt install cgit fcgiwrap +( cd /etc/sites-enabled/ && ln -s ../sites-avaiable/git.$DOMAIN . ) +sudo systemctl restart nginx +# Make fail2ban notice the new log file. +sudo systemctl restart fail2ban +#+end_src + +**** "Idle" vs default branch +cgit struggles to guess what to print for the "Idle" column on the +index page when the default branch is not "master". [[https://lists.zx2c4.com/pipermail/cgit/2020-August/004515.html][Workarounds]]: + +- set =repo.defbranch=, +- update =agefile= with [[https://git.zx2c4.com/cgit/tree/contrib/hooks/post-receive.agefile][a post-receive hook]]. +** CGI +I like the idea of [[https://en.wikipedia.org/wiki/Common_Gateway_Interface#Using_CGI_scripts][CGI "scripts"]], i.e. having the web server fire a +program to handle requests: + +- URI bits are passed through environment variables and stdin; +- the program spits the page on stdout. + +Had some fun toying with Python's ~cgi~ module; sadly though the +project has [[https://peps.python.org/pep-0594/#cgi][decided to deprecate it]]. The docs [[https://docs.python.org/3.11/library/cgi.html][suggest some migration +paths]], and there's a [[https://pypi.org/project/legacy-cgi/][legacy-cgi package on PyPI]] if I really want to +keep using it I guess. + +Also nginx has no support for CGI either, though their documentation +explains how to combine their FastCGI support with ~fcgiwrap~ to +enable CGI scripts. diff --git a/guides/writing.md b/guides/writing.md deleted file mode 100644 index 4231353..0000000 --- a/guides/writing.md +++ /dev/null @@ -1,45 +0,0 @@ -I worry a lot about how efficient my writing is. I want to keep it as -straightforward (maximizing signal-to-noise ratio) and accurate -(citing sources and assumptions) as I can. - -Since I keep catching myself making the same mistakes, and I do not -have automated tools to warn me about them yet, I figured I should -write down all the things I want to watch out for. - -# Decruftification - -## Sentences - -- Watch out for verbs which hide the action (e.g. try, allow). -- Use simpler words as long as they are not more vague. - -## Mails - -- Pick your battles: people only have time for so many subjects. - Focus on symptomatic relief; hint at systemic problems but wait - until prompted before ranting. -- Tricks to make mails more digestible: - - condense the point(s) into a tl;dr; - - move details (investigation, alternatives, rationale) into - footnotes or attachments; - - spin new threads. - -# Logic - -When expressing causation, check that all causes have been explicitly -stated. Be wary of assumptions^[I felt like adding "protect yourself -against misquotations", but those will probably happen anyway]. - -Conversely, if C can be deduced from A alone, consider rewriting -"since A and B, C" into "since A, C". Unnecessary Bs can be -detrimental to the discussion and make it to go off-topic; beware: - -- humorous overstatements that might be taken seriously; -- subconscious attempts at shoehorning a point that feels essential - but is not actually relevant. - -# Resources - -- [IEEE's "Write Clearly and Concisely"](http://sites.ieee.org/pcs/communication-resources-for-engineers/style/write-clearly-and-concisely/) -- [Gitlab's Technical Writing Handbook](https://about.gitlab.com/handbook/product/technical-writing/) -- [Lessons from my PhD — Austin Z. Henley](https://austinhenley.com/blog/lessonsfrommyphd.html) -- cgit v1.2.3