aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--TODOs.md11
-rwxr-xr-xsrc/bin/pb4
-rw-r--r--v2/.envrc2
-rw-r--r--v2/.gitignore15
-rw-r--r--v2/Makefile16
l---------v2/TODOs.md1
-rwxr-xr-xv2/aux/ci/git-post-receive.sh186
-rwxr-xr-xv2/aux/ci/git-pre-receive.sh14
-rwxr-xr-xv2/aux/ci/report.sh250
-rw-r--r--v2/aux/domain.txt1
-rw-r--r--v2/aux/lib.sh37
-rwxr-xr-xv2/aux/po4a-cfg.sh109
l---------v2/aux/workflow/TODOs.sh1
-rw-r--r--v2/aux/workflow/favicon.html1
l---------v2/aux/workflow/preamble.md1
l---------v2/aux/workflow/style.css1
-rw-r--r--v2/dynamic.mk104
-rw-r--r--v2/po/euandre.org.pot72
-rw-r--r--v2/po/po4a.cfg7
-rw-r--r--v2/po/pt.po53
-rwxr-xr-xv2/src/bin/absolute4
-rwxr-xr-xv2/src/bin/extract133
-rwxr-xr-xv2/src/bin/makemake137
-rwxr-xr-xv2/src/bin/url-for10
-rw-r--r--v2/src/content/.well-known/security.txt4
l---------v2/src/content/favicon.svg1
-rw-r--r--v2/src/content/pastebins/raku-tuple-type-annotation.md37
-rw-r--r--v2/src/content/pastebins/sicp-exercise-3-19.md10
-rw-r--r--v2/src/content/public.asc.txt86
l---------v2/src/content/static/atom.svg1
l---------v2/src/content/static/envelope.svg1
l---------v2/src/content/static/favicon.svg1
l---------v2/src/content/static/link.svg1
l---------v2/src/content/static/lock.svg1
l---------v2/src/content/static/public.asc.txt1
-rw-r--r--v2/src/content/style.css (renamed from v2/src/content/static/styles.css)0
-rw-r--r--v2/src/content/tils/lisp-three-way-conditional.md63
-rw-r--r--v2/src/development/config.env.in6
-rwxr-xr-xv2/src/development/dynmake.sh79
-rwxr-xr-xv2/src/development/genhtml.sh68
-rwxr-xr-xv2/src/development/getconf.sh119
-rw-r--r--v2/src/development/lib.sh33
-rwxr-xr-xv2/src/development/security-txt.sh.in82
-rw-r--r--v2/src/lib/base-conf.in9
-rw-r--r--v2/src/lib/base.en.conf3
-rw-r--r--v2/src/lib/base.pt.conf3
-rw-r--r--v2/src/lib/postamble.en.html7
-rw-r--r--v2/src/lib/postamble.pt.html5
-rw-r--r--v2/src/lib/preamble.en.html37
-rw-r--r--v2/src/lib/preamble.pt.html36
50 files changed, 1536 insertions, 328 deletions
diff --git a/TODOs.md b/TODOs.md
index 035bfc7..343f1dc 100644
--- a/TODOs.md
+++ b/TODOs.md
@@ -514,3 +514,14 @@ Because of subtitle embedding.
- <https://github.com/hbldh/hitherdither>
# Scratch
+
+
+Test editing files with ":set wm=80"
+Update static/attachments/autoqemu.tar.gz and other files
+mktorrent
+
+Future-proof wesite (and also later software):
+- remove Jekyll
+- remove markdown, or embed markdown process, and write in HTML directly (and maybe commit the HTML)
+- commit directly most derived data, such as torrent files, ogg media, SVG from graphviz input, etc., so that the absence of those tools don't impede the generation of existing pages of the website.
+Move brinquedoteca out of jekyll?
diff --git a/src/bin/pb b/src/bin/pb
index efbf8fc..48979e2 100755
--- a/src/bin/pb
+++ b/src/bin/pb
@@ -134,8 +134,8 @@ SLUG="${SLUG:-$(echo "$TITLE" | slugify)}"
OUT="$(outname "$DATE" "$SLUG")"
URL="$(url "$DATE" "$SLUG")"
-# shellcheck disable=2064
-trap "rm -f '$OUT-tmp'" EXIT
+trap 'rm -f "$OUT-tmp"' EXIT
+# shellcheck disable=2016
cat <<-EOF | e > "$OUT-tmp"
---
diff --git a/v2/.envrc b/v2/.envrc
index 1bdbb88..aa81005 100644
--- a/v2/.envrc
+++ b/v2/.envrc
@@ -2,3 +2,5 @@
set -eu
export PATH="$PWD/src/bin:$PATH"
+export GIT_DIR="$PWD"/../.git
+export GIT_WORK_TREE="$PWD"/../
diff --git a/v2/.gitignore b/v2/.gitignore
index 4ce7eaa..505c08b 100644
--- a/v2/.gitignore
+++ b/v2/.gitignore
@@ -1,9 +1,8 @@
-/src/development/config.env
/generated.mk
-/src/content/TODOs.html
-/aux/tld.txt
-/src/content/.well-known/security.txt
-
-/src/content/pastebins/*.html
-/src/content/pastebins/*.txt
-/src/content/pastebins/*.entry-*
+/*.sentinel
+/src/lib/base-conf
+/src/development/security-txt.sh
+/public/
+/src/content/*/*.conf
+/src/content/*/*.content
+/src/content/*/*.html
diff --git a/v2/Makefile b/v2/Makefile
index b0acb8f..e7a7279 100644
--- a/v2/Makefile
+++ b/v2/Makefile
@@ -1,12 +1,18 @@
.POSIX:
.DEFAULT:
- $(MAKE) generated.mk
- $(MAKE) -f dynamic.mk $<
+ @$(MAKE) deps
+ @$(MAKE) -f dynamic.mk $<
-all: generated.mk
- $(MAKE) -f dynamic.mk all
+all: deps
+ @$(MAKE) -f dynamic.mk all
+
+deps: generated.mk po/po4a.cfg
generated.mk: ALWAYS
- sh src/development/dynmake.sh > $@
+ @sh src/bin/makemake > generated.mk
+
+po/po4a.cfg: ALWAYS
+ @sh aux/po4a-cfg.sh | ifnew $@
+ po4a $@
ALWAYS:
diff --git a/v2/TODOs.md b/v2/TODOs.md
deleted file mode 120000
index 090bb76..0000000
--- a/v2/TODOs.md
+++ /dev/null
@@ -1 +0,0 @@
-../TODOs.md \ No newline at end of file
diff --git a/v2/aux/ci/git-post-receive.sh b/v2/aux/ci/git-post-receive.sh
new file mode 100755
index 0000000..76adccf
--- /dev/null
+++ b/v2/aux/ci/git-post-receive.sh
@@ -0,0 +1,186 @@
+#!/bin/sh
+# shellcheck source=/dev/null disable=2317
+. /etc/rc
+set -eu
+
+
+# shellcheck disable=2034
+read -r _oldrev SHA REFNAME
+
+if [ "$SHA" = '0000000000000000000000000000000000000000' ]; then
+ exit
+fi
+
+
+SKIP_DEPLOY=false
+for n in $(seq 0 $((GIT_PUSH_OPTION_COUNT - 1))); do
+ opt="$(eval "printf '%s' \"\$GIT_PUSH_OPTION_$n\"")"
+ case "$opt" in
+ ci.skip)
+ cat <<-EOF
+
+ "$opt" option detected, not running CI.
+
+ EOF
+ exit
+ ;;
+ deploy.skip)
+ SKIP_DEPLOY=true
+ ;;
+ *)
+ ;;
+ esac
+done
+
+
+epoch() {
+ awk 'BEGIN { srand(); print(srand()); }'
+}
+
+now() {
+ date '+%Y-%m-%dT%H:%M:%S%:z'
+}
+
+NAME="$(basename "$PWD" .git)"
+LOGS_DIR=/var/log/ci/"$NAME"/
+HTML_OUTDIR="/srv/www/s/$NAME"
+TIMESTAMP="$(now)"
+FILENAME="$TIMESTAMP-$SHA.log"
+LOGFILE="$LOGS_DIR/$FILENAME"
+mkdir -p "$LOGS_DIR"
+
+
+END_MARKER='\033[0m'
+LIGHT_BLUE_B='\033[1;36m'
+YELLOW='\033[1;33m'
+
+blue() {
+ printf "${LIGHT_BLUE_B}%s${END_MARKER}" "$1"
+}
+
+yellow() {
+ printf "${YELLOW}%s${END_MARKER}" "$1"
+}
+
+info() {
+ sed "s|^\(.\)|$(blue 'CI'): \1|"
+}
+
+
+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"
+}
+
+
+{
+ cat <<-EOF | info
+ Starting CI job at: $(now)
+ EOF
+ START="$(epoch)"
+
+ duration() {
+ if [ "$RUN_DURATION" -gt 60 ]; then
+ cat <<-EOF
+ $(yellow 'WARNING'): run took more than 1 minute! ($RUN_DURATION seconds)
+ EOF
+ else
+ cat <<-EOF
+ Run took $RUN_DURATION seconds.
+ EOF
+ fi
+ }
+
+ finish() {
+ STATUS="$?"
+ END="$(epoch)"
+ RUN_DURATION=$((END - START))
+ cat <<-EOF | info
+ Finishing CI job at: $(now)
+ Exit status was $STATUS
+ Re-run with:
+ \$ $CMD
+ $(duration)
+ EOF
+
+ NOTE="$(
+ cat <<-EOF
+ See CI logs with:
+ git notes --ref=refs/notes/ci-logs show $SHA
+ git notes --ref=refs/notes/ci-data show $SHA
+
+ Exit status: $STATUS
+ Duration: $RUN_DURATION
+ EOF
+ )"
+ git notes --ref=refs/notes/ci-data add -f -m "$(
+ cat <<-EOF
+ status $STATUS
+ sha $SHA
+ filename $FILENAME
+ duration $RUN_DURATION
+ timestamp $TIMESTAMP
+ to-prod $TO_PROD
+ refname $REFNAME
+ EOF
+ )" "$SHA"
+ git notes --ref=refs/notes/ci-logs add -f -F "$LOGFILE" "$SHA"
+ git notes add -f -m "$NOTE" "$SHA"
+
+ {
+ printf 'Git CI HTML report for %s (%s) started.\n' "$NAME" "$SHA" >&2
+ DIR="$(mkdtemp)"
+ REMOTE="$PWD"
+ cd "$DIR"
+ {
+ git clone "$REMOTE" .
+ git fetch origin 'refs/notes/*:refs/notes/*'
+ } 1>/dev/null 2>&1
+ sh aux/ci/report.sh -n "$NAME" -o public-ci
+ sudo -u deployer mkdir -p "$HTML_OUTDIR"/ci/
+ sudo -u deployer rsync \
+ --chmod=D775,F664 \
+ --chown=deployer:deployer \
+ --delete \
+ -a \
+ public-ci/ "$HTML_OUTDIR"/ci/
+ rm -rf "$DIR"
+ printf 'Git CI HTML report for %s (%s) finished.\n' "$NAME" "$SHA" >&2
+ } 2>&1 | logger -i -p local0.warn -t git-ci 1>/dev/null 2>&1 &
+ }
+ trap finish EXIT
+
+ unset GIT_DIR
+
+ if [ "$REFNAME" = 'refs/heads/main' ] && [ "$SKIP_DEPLOY" = false ]; then
+ cat <<-EOF | info
+ In branch "main", running deploy for $SHA.
+ EOF
+ TO_PROD=true
+ CMD="sudo cicd $NAME $SHA"
+ else
+ if [ "$SKIP_DEPLOY" = true ]; then
+ cat <<-EOF | info
+ "deploy.skip" option detected, skipping deploy for $SHA.
+ EOF
+ else
+ cat <<-EOF | info
+ Not on branch "main", skipping deploy for $SHA.
+ EOF
+ fi
+ TO_PROD=false
+ CMD="sudo cicd -n $NAME $SHA"
+ fi
+ $CMD
+} 2>&1 | ts -s '%.s' | tee "$LOGFILE"
diff --git a/v2/aux/ci/git-pre-receive.sh b/v2/aux/ci/git-pre-receive.sh
new file mode 100755
index 0000000..199d06e
--- /dev/null
+++ b/v2/aux/ci/git-pre-receive.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+set -eu
+
+read -r _oldrev SHA _refname
+unset GIT_DIR
+
+if [ "$SHA" = '0000000000000000000000000000000000000000' ]; then
+ exit
+fi
+
+printf 'Upgrading post-receive hook...' >&2
+git show "$SHA":aux/ci/git-post-receive.sh > hooks/post-receive
+chmod +x hooks/post-receive
+printf 'done.\n' >&2
diff --git a/v2/aux/ci/report.sh b/v2/aux/ci/report.sh
new file mode 100755
index 0000000..0a0a0ae
--- /dev/null
+++ b/v2/aux/ci/report.sh
@@ -0,0 +1,250 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ aux/ci/report.sh -o OUTDIR
+ aux/ci/report.sh -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+
+ Options:
+ -o OUTDIR 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, ran for
+ 15 seconds and was deployed to production, the expected output on the
+ target directory "public" is:
+
+ $ tree public/
+ public/
+ index.html
+ data/
+ 2020-01-01T01:00:00-deadbeef.log
+ ...
+ logs/
+ 2020-01-01T01:00:00-deadbeef.log
+ ...
+
+ $ cat public/data/2020-01-01T01:00:00-deadbeef.log
+ status 0
+ sha deadbeef
+ filename deadbeef 2020-01-01T01:00:00-deadbeef.log
+ duration 15
+ timestamp 2020-01-01T01:00:00
+ to-prod true
+ refname refs/heads/main
+
+ $ cat public/logs/2020-01-01T01:00:00-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 and data files.
+
+ 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:
+
+ $ sh aux/ci/report.sh -o www
+ EOF
+}
+
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'o:h' flag; do
+ case "$flag" in
+ o)
+ OUTDIR="$OPTARG"
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+. aux/lib.sh
+
+eval "$(assert_arg "${OUTDIR:-}" '-o OUTDIR')"
+
+
+esc() {
+ sed \
+ -e 's|&|\&amp;|g' \
+ -e 's|<|\&lt;|g' \
+ -e 's|>|\&gt;|g' \
+ -e 's|"|\&quot;|g' \
+ -e "s|'|\&#39;|g"
+}
+
+mkdir -p "$OUTDIR"
+cd "$OUTDIR"
+mkdir -p logs data
+
+for c in $(git notes list | cut -d' ' -f2); do
+ git 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 notes --ref=refs/notes/ci-logs show "$c" > logs/"$FILENAME"
+done
+
+{
+ cat <<-EOF
+ <!DOCTYPE html>
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <meta name="description" content="CI logs for $NAME" />
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
+ <title>$NAME - CI logs</title>
+ <style>
+ body {
+ max-width: 800px;
+ margin: 0 auto;
+ }
+
+ code {
+ display: block;
+ margin: 1em 0em 3em 3em;
+ overflow: auto;
+ }
+
+ pre {
+ display: inline;
+ }
+
+ ol {
+ list-style-type: disc;
+ }
+
+ pre, code {
+ background-color: #ddd;
+ }
+
+ @media(prefers-color-scheme: dark) {
+ :root {
+ color: white;
+ background-color: black;
+ }
+
+ a {
+ color: hsl(211, 100%, 60%);
+ }
+
+ a:visited {
+ color: hsl(242, 100%, 80%);
+ }
+
+ pre, code {
+ background-color: #222;
+ }
+ }
+ </style>
+ </head>
+ <body>
+ <main>
+ <h1>
+ CI logs for
+ <a href="https://$DOMAIN/s/$NAME/">$NAME</a>
+ </h1>
+ <ol>
+ EOF
+
+
+ PASS='&#x2705;' # ✅
+ WARN='&#x1F40C;' # 🐌
+ FAIL='&#x274C;' # ❌
+ 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 log -1 --format=%B "$SHA" || {
+ git fetch origin "$SHA"
+ git 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
+ <li id="$FILENAME">
+ <a href="#$FILENAME"><pre>#</pre></a>
+ $STATUS_MARKER - <pre>${DURATION:-?}s</pre>
+ <pre>(<a href="https://$DOMAIN/git/$NAME/commit/?id=$SHA">commit</a>)</pre>
+ <a href="logs/$FILENAME"><pre>$FILENAME</pre></a>
+ <pre>(<a href="data/$FILENAME">data</a>)</pre>
+ <br />
+ <code><pre>$MESSAGE</pre></code>
+ </li>
+ EOF
+ done
+
+ cat <<-EOF
+ </ol>
+ </main>
+ </body>
+ </html>
+ EOF
+} > index.html
diff --git a/v2/aux/domain.txt b/v2/aux/domain.txt
new file mode 100644
index 0000000..fd7ea0f
--- /dev/null
+++ b/v2/aux/domain.txt
@@ -0,0 +1 @@
+euandre.org
diff --git a/v2/aux/lib.sh b/v2/aux/lib.sh
new file mode 100644
index 0000000..93a37eb
--- /dev/null
+++ b/v2/aux/lib.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+DOMAIN="$(cat aux/domain.txt)"
+NAME="$(basename "$PWD")"
+export DOMAIN NAME
+
+assert_arg() {
+ if [ -z "$1" ]; then
+ printf 'Missing %s.\n\n' "$2" >&2
+ cat <<-'EOF'
+ usage >&2
+ exit 2
+ EOF
+ fi
+}
+
+uuid() {
+ od -xN20 /dev/urandom |
+ head -n1 |
+ awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}'
+}
+
+tmpname() {
+ printf '%s/uuid-tmpname-without-spaces.%s' "${TMPDIR:-/tmp}" "$(uuid)"
+}
+
+mkstemp() {
+ name="$(tmpname)"
+ touch -- "$name"
+ printf '%s' "$name"
+}
+
+mkdtemp() {
+ name="$(tmpname)"
+ mkdir -p -- "$name"
+ printf '%s' "$name"
+}
diff --git a/v2/aux/po4a-cfg.sh b/v2/aux/po4a-cfg.sh
new file mode 100755
index 0000000..b20e303
--- /dev/null
+++ b/v2/aux/po4a-cfg.sh
@@ -0,0 +1,109 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ aux/po4a-cfg.sh > po/po4a.cfg
+ aux/po4a-cfg.sh -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+
+ Options:
+ -h, --help show this message
+
+
+ Discover translatable files in the repository (via
+ git-ls-files(1)) that have '.en.' or '/en/' in their name and
+ emit the configuration file to be used with po4a(1).
+
+
+ Examples:
+
+ Setup i18n on a new repository:
+
+ $ mkdir po
+ $ touch po/pt.po
+ $ touch po/"$(basename "$PWD")".pot
+ $ aux/po4a-cfg.sh > po/po4a.cfg
+ $ po4a po/po4a.cfg
+
+
+ Conditionally update the configuration in a Makefile:
+
+ po/po4a.cfg: ALWAYS
+ @sh aux/po4a-cfg.sh | ifnew $@
+ po4a $@
+ 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))
+
+. aux/lib.sh
+
+
+guess_type() {
+ case "$1" in
+ *.md)
+ echo text
+ ;;
+ *.[1-9]*.in)
+ echo man
+ ;;
+ *.html)
+ echo xhtml
+ ;;
+ *)
+ echo text
+ ;;
+ esac
+}
+
+
+cat <<-'EOF'
+ [options] --keep 0 --master-charset UTF-8 --localized-charset UTF-8
+
+ [po_directory] po
+
+EOF
+
+git ls-files | grep -F '.en.' | while read -r file; do
+ TYPE="$(guess_type "$file")"
+ # shellcheck disable=2016
+ VAR_FILE="$(printf '%s' "$file" | sed 's|\.en\.|.$lang.|')"
+ # shellcheck disable=2016
+ printf '[type: %s] %s $lang:%s\n' "$TYPE" "$file" "$VAR_FILE"
+done
diff --git a/v2/aux/workflow/TODOs.sh b/v2/aux/workflow/TODOs.sh
deleted file mode 120000
index aaa6c50..0000000
--- a/v2/aux/workflow/TODOs.sh
+++ /dev/null
@@ -1 +0,0 @@
-../../../aux/workflow/TODOs.sh \ No newline at end of file
diff --git a/v2/aux/workflow/favicon.html b/v2/aux/workflow/favicon.html
deleted file mode 100644
index 6f1bac8..0000000
--- a/v2/aux/workflow/favicon.html
+++ /dev/null
@@ -1 +0,0 @@
-<link rel="icon" type="image/svg+xml" href="static/favicon.svg" />
diff --git a/v2/aux/workflow/preamble.md b/v2/aux/workflow/preamble.md
deleted file mode 120000
index 739f3ea..0000000
--- a/v2/aux/workflow/preamble.md
+++ /dev/null
@@ -1 +0,0 @@
-../../../aux/workflow/preamble.md \ No newline at end of file
diff --git a/v2/aux/workflow/style.css b/v2/aux/workflow/style.css
deleted file mode 120000
index ffff132..0000000
--- a/v2/aux/workflow/style.css
+++ /dev/null
@@ -1 +0,0 @@
-../../../aux/workflow/style.css \ No newline at end of file
diff --git a/v2/dynamic.mk b/v2/dynamic.mk
index 8abb62e..d31be9f 100644
--- a/v2/dynamic.mk
+++ b/v2/dynamic.mk
@@ -1,7 +1,6 @@
.POSIX:
-FQDN = euandre.org
-PORT = 4444
-BASE_URL =
+DOMAIN = euandre.org
+EMAIL = eu@euandre.org
default: all
@@ -11,73 +10,90 @@ include generated.mk
.SUFFIXES:
-.SUFFIXES: .md .html .in .entry-content .entry-env
-
+.SUFFIXES: .in .md .conf .content .html
.in:
sed \
- -e 's|@FQDN@|$(FQDN)|g' \
- -e 's|@BASE_URL@|$(BASE_URL)|g' \
+ -e 's|@DOMAIN@|$(DOMAIN)|g' \
+ -e 's|@EMAIL@|$(EMAIL)|g' \
< $< > $@
if [ -x $< ]; then chmod +x $@; fi
+.md.conf:
+ sh src/development/getconf.sh $< > $@
+
+.md.content:
+ awk 'sep >= 2; /^---$$/ {sep++}' < $< > $@
+
.md.html:
sh src/development/genhtml.sh $< > $@
-.md.entry-content:
- extract -t content $? > $@
-.md.entry-env:
- extract -t env $? > $@
+favicons = \
+ public/favicon.png \
+ public/favicon.ico \
+derived-assets = \
+ $(all-generated) \
+ $(favicons) \
+ src/lib/base-conf \
+ src/development/security-txt.sh \
-pastebins.html = $(pastebins.md:.md=.html)
+$(all-generated.conf) $(all-generated.content): $(non-content)
-html = \
- $(pastebins.html) \
+all: public
+$(all-generated.conf): src/lib/base-conf
-ALL = \
- src/content/TODOs.html \
- $(html) \
-
+clean:
+ rm -rf \
+ public/ $(derived-assets) *.sentinel generated.mk
-all: $(ALL)
+public: $(favicons) public-copy-content.sentinel
-$(html) src/bin/absolute src/bin/extract src/bin/url-for: src/development/config.env
-$(html): src/development/genhtml.sh
+public-mkdir.sentinel:
+ mkdir -p public
+ touch $@
+public/favicon.png: public-mkdir.sentinel src/content/favicon.svg
+ inkscape -o $@ -w 2048 -h 2048 -b white src/content/favicon.svg
-collections = pastebins
+public/favicon.ico: public-mkdir.sentinel src/content/favicon.svg
+ convert src/content/favicon.svg $@
-clean:
- for c in $(collections); do \
- rm -f \
- src/content/$$c/*.entry-* \
- src/content/$$c/*.txt; \
- done
- rm -rf \
- $(ALL) generated.mk src/development/config.env \
+public-copy-content.sentinel: $(all-generated.html) $(static-content) \
+ src/content/public.asc.txt src/content/.well-known/security.txt
+ echo $? | \
+ tr ' ' '\n' | \
+ sed 's|^src/content/||' | \
+ tee $@-tmp | \
+ xargs dirname | \
+ sort | \
+ uniq | \
+ xargs -P`nproc` -I% mkdir -p public/%
+ xargs -P`nproc` -I% cp src/content/% public/% < $@-tmp
+ rm -f $@-tmp
+ touch $@
-src/content/TODOs.html: TODOs.md
- sh aux/workflow/TODOs.sh -n website -m public-inbox > $@
+src/content/public.asc.txt:
+ gpg --armour --export '$(EMAIL)' > $@
-public: all
+src/content/.well-known/security.txt: src/content/public.asc.txt src/development/security-txt.sh
+ sh src/development/security-txt.sh > $@
check:
-dev-check: check
-
-fqdn:
- printf '$(FQDN)'
-
-
+dev: check
run: all
- open 'http://localhost:$(PORT)'
- serve -d src/content/ -p $(PORT)
-
-deploy: all
- rsync -avzP src/content/ $(FQDN):/home/user-data/www/default/v2/ --delete
+ serve -d public/
+
+upload: public
+ rsync \
+ --rsync-path='sudo -u deployer rsync' \
+ -avzP \
+ --delete \
+ --exclude 's/*' \
+ public/ $(DOMAIN):/srv/www/
diff --git a/v2/po/euandre.org.pot b/v2/po/euandre.org.pot
new file mode 100644
index 0000000..d3613c2
--- /dev/null
+++ b/v2/po/euandre.org.pot
@@ -0,0 +1,72 @@
+# SOME DESCRIPTIVE TITLE
+# Copyright (C) YEAR Free Software Foundation, Inc.
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"POT-Creation-Date: 2023-04-04 06:28-0300\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. type: Plain text
+#: src/lib/base.en.conf:2
+msgid "date_fmt='%B %-d, %Y'"
+msgstr ""
+
+#. type: Plain text
+#: src/lib/base.en.conf:3
+msgid "site_name=\"EuAndreh's website\""
+msgstr ""
+
+#. type: Content of: <p>
+#: src/lib/postamble.en.html:2
+msgid ""
+"<a href=\"@mailto_uri@\">Comment</a> and see <a "
+"href=\"@discussions_url@\">existing discussions</a> | <a "
+"href=\"@sourcecode_url@\">view source</a>"
+msgstr ""
+
+#. type: Attribute 'lang' of: <html>
+#: src/lib/preamble.en.html:2
+msgid "$lang"
+msgstr ""
+
+#. type: Content of: <html><head><title>
+#: src/lib/preamble.en.html:9
+msgid "$(htmlesc \"$title\")"
+msgstr ""
+
+#. type: Content of: <html><body><header><nav><ul>
+#: src/lib/preamble.en.html:23
+msgid ""
+"<a href=\"$lang_url\">EuAndreh</a> <a href=\"$(url-for "
+"'about.html')\">About</a>"
+msgstr ""
+
+#. type: Content of: <html><body><main><article><h1>
+#: src/lib/preamble.en.html:32
+msgid "$title_html"
+msgstr ""
+
+#. type: Content of: <html><body><main><article><p>
+#: src/lib/preamble.en.html:35
+msgid "Posted on"
+msgstr ""
+
+#. type: Content of: <html><body><main><article><p><time>
+#: src/lib/preamble.en.html:35
+msgid "$date_formatted"
+msgstr ""
+
+#. type: Content of: <html><body><main><article>
+#: src/lib/preamble.en.html:37
+msgid "EOF"
+msgstr ""
diff --git a/v2/po/po4a.cfg b/v2/po/po4a.cfg
new file mode 100644
index 0000000..877f5a9
--- /dev/null
+++ b/v2/po/po4a.cfg
@@ -0,0 +1,7 @@
+[options] --keep 0 --master-charset UTF-8 --localized-charset UTF-8
+
+[po_directory] po
+
+[type: text] src/lib/base.en.conf $lang:src/lib/base.$lang.conf
+[type: xhtml] src/lib/postamble.en.html $lang:src/lib/postamble.$lang.html
+[type: xhtml] src/lib/preamble.en.html $lang:src/lib/preamble.$lang.html
diff --git a/v2/po/pt.po b/v2/po/pt.po
new file mode 100644
index 0000000..2eb3ca6
--- /dev/null
+++ b/v2/po/pt.po
@@ -0,0 +1,53 @@
+#. type: Plain text
+#: src/lib/base.en.conf:2
+msgid "date_fmt='%B %-d, %Y'"
+msgstr ""
+
+#. type: Plain text
+#: src/lib/base.en.conf:3
+msgid "site_name=\"EuAndreh's website\""
+msgstr ""
+
+#. type: Content of: <p>
+#: src/lib/postamble.en.html:2
+msgid ""
+"<a href=\"@mailto_uri@\">Comment</a> and see <a href=\"@discussions_url@"
+"\">existing discussions</a> | <a href=\"@sourcecode_url@\">view source</a>"
+msgstr ""
+
+#. type: Attribute 'lang' of: <html>
+#: src/lib/preamble.en.html:2
+msgid "$lang"
+msgstr ""
+
+#. type: Content of: <html><head><title>
+#: src/lib/preamble.en.html:9
+msgid "$(htmlesc \"$title\")"
+msgstr ""
+
+#. type: Content of: <html><body><header><nav><ul>
+#: src/lib/preamble.en.html:23
+msgid ""
+"<a href=\"$lang_url\">EuAndreh</a> <a href=\"$(url-for 'about."
+"html')\">About</a>"
+msgstr ""
+
+#. type: Content of: <html><body><main><article><h1>
+#: src/lib/preamble.en.html:32
+msgid "$title_html"
+msgstr ""
+
+#. type: Content of: <html><body><main><article><p>
+#: src/lib/preamble.en.html:35
+msgid "Posted on"
+msgstr ""
+
+#. type: Content of: <html><body><main><article><p><time>
+#: src/lib/preamble.en.html:35
+msgid "$date_formatted"
+msgstr ""
+
+#. type: Content of: <html><body><main><article>
+#: src/lib/preamble.en.html:37
+msgid "EOF"
+msgstr ""
diff --git a/v2/src/bin/absolute b/v2/src/bin/absolute
index ae25b43..6434219 100755
--- a/v2/src/bin/absolute
+++ b/v2/src/bin/absolute
@@ -62,6 +62,6 @@ done
shift $((OPTIND - 1))
-. src/development/config.env
+. src/lib/base-conf
-printf 'https://%s%s' "$FQDN" "$(cat)"
+printf 'https://%s%s' "$domain" "$(cat)"
diff --git a/v2/src/bin/extract b/v2/src/bin/extract
deleted file mode 100755
index 7bbcba7..0000000
--- a/v2/src/bin/extract
+++ /dev/null
@@ -1,133 +0,0 @@
-#!/bin/sh
-set -eu
-
-
-usage() {
- cat <<-'EOF'
- Usage:
- extract -t TYPE FILENAME
- extract -h
- EOF
-}
-
-help() {
- cat <<-'EOF'
-
- Options:
- -t TYPE the type of extraction to perform ("content" or "env")
- -h, --help show this message
-
- FILENAME the name of the input file, also to be used as
- URL.
-
-
- Separate the content from the "frontmatter", and emit the
- selected one, given the FILENAME.
-
-
- Examples:
-
- Get the content:
-
- $ extract -t content src/file.md > src/file.entry-content
-
-
- Get the "frontmatter":
-
- $ extract -t env src/f.md > src/f.entry-env
- EOF
-}
-
-
-for flag in "$@"; do
- case "$flag" in
- --)
- break
- ;;
- --help)
- usage
- help
- exit
- ;;
- *)
- ;;
- esac
-done
-
-TYPE=''
-while getopts 't:h' flag; do
- case "$flag" in
- t)
- TYPE="$OPTARG"
- ;;
- h)
- usage
- help
- exit
- ;;
- *)
- usage >&2
- exit 2
- ;;
- esac
-done
-shift $((OPTIND - 1))
-
-
-FILENAME="${1:-}"
-eval "$(assert-arg "$FILENAME" 'FILENAME')"
-eval "$(assert-arg "$TYPE" '-t TYPE')"
-
-
-case "$TYPE" in
- content)
- . "${FILENAME%.md}.entry-env"
- printf '%s\n' "$PREAMBLE"
- awk '
- separator >= 2
- /^---$/ { separator++ }
- ' "$FILENAME"
- ;;
- env)
- cat src/development/config.env
- awk '
- /^---$/ {
- if (++separator > 1) {
- exit
- } else {
- next
- }
- }
-
- { print }
- ' "$FILENAME"
- printf "FILENAME='%s'\n" "$FILENAME"
- cat <<-'REAL_EOF'
- TITLE="${TITLE:-$SITE_NAME}"
-
- URI_TITLE="$(printf '%s' "$TITLE" | uri)"
-
- URL="$(
- printf '%s' "$FILENAME" |
- sed \
- -e 's|^src/content/||' \
- -e 's|md$|html|'
- )"
-
- PREAMBLE="$(cat <<EOF
- # $TITLE
-
- <p class="timestamp">
- Posted on <time datetime="$DATE">$(LANG="$LANGUAGE" date -d "$DATE" "$DATE_FMT")</time>
- </p>
- EOF
- )"
- REAL_EOF
- ;;
- *)
- printf 'Bad value for TYPE: "%s".\n\n' \
- "$TYPE" >&2
- usage >&2
- exit 2
- ;;
-esac
diff --git a/v2/src/bin/makemake b/v2/src/bin/makemake
new file mode 100755
index 0000000..f4ab87a
--- /dev/null
+++ b/v2/src/bin/makemake
@@ -0,0 +1,137 @@
+#!/bin/sh
+set -eu
+
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ makemake
+ makemake -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help show this message
+
+
+ Generate make(1) code for later evaluation by make(1). What
+ this scripts does is fill the gap where make(1) can't handle
+ globs and dynamic dependencies, and uses some ad-hoc scripts
+ to generate those.
+ 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))
+
+
+
+varlist() {
+ printf '%s = \\\n' "$1"
+ sed \
+ -e 's/^/ /' \
+ -e 's/$/ \\/'
+ printf '\n'
+}
+
+html_deps() {
+ "$@" | sed 's/^\(.*\)\.md$/\1.conf \1.content: \1.md/'
+ "$@" | sed 's/^\(.*\)\.md$/\1.html: \1.conf \1.content/'
+ printf '\n'
+}
+
+
+content_mds() {
+ find src/content/"$1"/ -type f -name '*.md'
+}
+
+RESOURCES='
+pastebins
+tils
+'
+
+EXTENSIONS='
+.md
+.conf
+.content
+.prehtml
+.posthtml
+.html
+'
+
+for r in $RESOURCES; do
+ content_mds "$r" | varlist "$r".md
+ for e in $EXTENSIONS; do
+ if [ "$e" = '.md' ]; then
+ continue
+ fi
+ printf '%s%s = $(%s.md:.md=%s)\n' "$r" "$e" "$r" "$e"
+ done
+ printf '%s =' "$r"
+ for e in $EXTENSIONS; do
+ if [ "$e" = '.md' ]; then
+ continue
+ fi
+ printf ' $(%s%s)' "$r" "$e"
+ done
+ printf '\n'
+ html_deps content_mds "$r"
+ printf '\n'
+done
+
+all_resources() {
+ echo $RESOURCES | tr ' ' '\n'
+}
+
+all_vars() {
+ EXT="$1"
+ all_resources |
+ sed 's|^|$(|' |
+ sed "s|$|$EXT)|" |
+ varlist all-generated"$EXT"
+}
+
+for e in $EXTENSIONS; do
+ all_vars "$e"
+done
+all_vars ''
+
+
+git ls-files |
+ grep -v '^src/content/' |
+ varlist 'non-content'
+
+git ls-files src/content/ |
+ grep -v '\.md$' |
+ grep -Ev "src/content/($(all_resources | paste -sd'|'))/" |
+ varlist 'static-content'
diff --git a/v2/src/bin/url-for b/v2/src/bin/url-for
index 025a546..c31ff84 100755
--- a/v2/src/bin/url-for
+++ b/v2/src/bin/url-for
@@ -1,7 +1,6 @@
#!/bin/sh
set -eu
-
usage() {
cat <<-'EOF'
Usage:
@@ -69,9 +68,12 @@ while getopts 'h' flag; do
done
shift $((OPTIND - 1))
+. src/development/lib.sh
+
+
FILE="${1:-}"
-eval "$(assert-arg "$FILE" 'FILE')"
+eval "$(assert_arg "$FILE" 'FILE')"
-. src/development/config.env
+. src/lib/base-conf
-printf '%s/%s' "$BASE_URL" "$FILE"
+printf '%s%s' "${base_url:-/}" "$FILE"
diff --git a/v2/src/content/.well-known/security.txt b/v2/src/content/.well-known/security.txt
new file mode 100644
index 0000000..dd35c49
--- /dev/null
+++ b/v2/src/content/.well-known/security.txt
@@ -0,0 +1,4 @@
+Contact: mailto:eu@euandre.org
+Encryption: https://euandre.org/public.asc.txt
+Expires: 2024-07-15T00:00:00z
+Preferred-Languages: en, pt
diff --git a/v2/src/content/favicon.svg b/v2/src/content/favicon.svg
new file mode 120000
index 0000000..313dcc5
--- /dev/null
+++ b/v2/src/content/favicon.svg
@@ -0,0 +1 @@
+../../../static/lord-favicon.svg \ No newline at end of file
diff --git a/v2/src/content/pastebins/raku-tuple-type-annotation.md b/v2/src/content/pastebins/raku-tuple-type-annotation.md
new file mode 100644
index 0000000..3d5ff34
--- /dev/null
+++ b/v2/src/content/pastebins/raku-tuple-type-annotation.md
@@ -0,0 +1,37 @@
+---
+
+title: Raku tuple type annotation
+
+date: 2019-12-29
+
+layout: post
+
+lang: en
+
+ref: raku-tuple-type-annotation
+
+---
+
+```perl
+# Single Str return value: this works
+sub f1(Str $in --> Str) {
+ $in;
+}
+
+# Tuple of Str as return value: this works
+sub f2(Str $in) {
+ ($in, $in);
+}
+
+# Tuple of Str as return value with type annotation: this doesn't works
+sub f2(Str $in --> (Str, Str)) {
+ ($in, $in);
+}
+```
+
+Error log is:
+
+```perl
+===SORRY!=== Error while compiling /path/to/my/file
+Malformed return value
+```
diff --git a/v2/src/content/pastebins/sicp-exercise-3-19.md b/v2/src/content/pastebins/sicp-exercise-3-19.md
index fd2c52b..8834889 100644
--- a/v2/src/content/pastebins/sicp-exercise-3-19.md
+++ b/v2/src/content/pastebins/sicp-exercise-3-19.md
@@ -1,14 +1,14 @@
---
-TITLE='SICP exercise 3.19'
+title: SICP exercise 3.19
-DATE='2021-09-02'
+date: 2021-09-02
-LAYOUT='post'
+layout: post
-LANGUAGE='en'
+lang: en
-REF='sicp-exercise-3-19'
+id: sicp-exercise-3-19
---
diff --git a/v2/src/content/public.asc.txt b/v2/src/content/public.asc.txt
new file mode 100644
index 0000000..533b54c
--- /dev/null
+++ b/v2/src/content/public.asc.txt
@@ -0,0 +1,86 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+LsPdAc9FGcUNEy+/AlLpM1Y=
+=2ZCp
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/v2/src/content/static/atom.svg b/v2/src/content/static/atom.svg
deleted file mode 120000
index 41c6d3f..0000000
--- a/v2/src/content/static/atom.svg
+++ /dev/null
@@ -1 +0,0 @@
-../../../../static/atom.svg \ No newline at end of file
diff --git a/v2/src/content/static/envelope.svg b/v2/src/content/static/envelope.svg
deleted file mode 120000
index bd0c577..0000000
--- a/v2/src/content/static/envelope.svg
+++ /dev/null
@@ -1 +0,0 @@
-../../../../static/envelope.svg \ No newline at end of file
diff --git a/v2/src/content/static/favicon.svg b/v2/src/content/static/favicon.svg
deleted file mode 120000
index 33566ab..0000000
--- a/v2/src/content/static/favicon.svg
+++ /dev/null
@@ -1 +0,0 @@
-../../../../static/lord-favicon.svg \ No newline at end of file
diff --git a/v2/src/content/static/link.svg b/v2/src/content/static/link.svg
deleted file mode 120000
index bf69c40..0000000
--- a/v2/src/content/static/link.svg
+++ /dev/null
@@ -1 +0,0 @@
-../../../../static/link.svg \ No newline at end of file
diff --git a/v2/src/content/static/lock.svg b/v2/src/content/static/lock.svg
deleted file mode 120000
index f9a4f33..0000000
--- a/v2/src/content/static/lock.svg
+++ /dev/null
@@ -1 +0,0 @@
-../../../../static/lock.svg \ No newline at end of file
diff --git a/v2/src/content/static/public.asc.txt b/v2/src/content/static/public.asc.txt
deleted file mode 120000
index 5175f38..0000000
--- a/v2/src/content/static/public.asc.txt
+++ /dev/null
@@ -1 +0,0 @@
-../../../../public.asc \ No newline at end of file
diff --git a/v2/src/content/static/styles.css b/v2/src/content/style.css
index 0ec67a8..0ec67a8 100644
--- a/v2/src/content/static/styles.css
+++ b/v2/src/content/style.css
diff --git a/v2/src/content/tils/lisp-three-way-conditional.md b/v2/src/content/tils/lisp-three-way-conditional.md
new file mode 100644
index 0000000..f53451b
--- /dev/null
+++ b/v2/src/content/tils/lisp-three-way-conditional.md
@@ -0,0 +1,63 @@
+---
+
+title: Three-way conditional for number signs on Lisp
+
+date: 2021-04-24 3
+
+updated_at: 2021-08-14
+
+layout: post
+
+lang: en
+
+ref: three-way-conditional-for-number-signs-on-lisp
+
+---
+
+A useful macro from Paul Graham's [On Lisp][on-lisp] book:
+
+```lisp
+(defmacro nif (expr pos zero neg)
+ (let ((g (gensym)))
+ `(let ((,g ,expr))
+ (cond ((plusp ,g) ,pos)
+ ((zerop ,g) ,zero)
+ (t ,neg)))))
+```
+
+After I looked at this macro, I started seeing opportunities to using it in many places, and yet I didn't see anyone else using it.
+
+The latest example I can think of is section 1.3.3 of [Structure and Interpretation of Computer Programs][sicp], which I was reading recently:
+
+```scheme
+(define (search f neg-point pos-point)
+ (let ((midpoint (average neg-point pos-point)))
+ (if (close-enough? neg-point post-point)
+ midpoint
+ (let ((test-value (f midpoint)))
+ (cond ((positive? test-value)
+ (search f neg-point midpoint))
+ ((negative? test-value)
+ (search f midpoint pos-point))
+ (else midpoint))))))
+```
+
+Not that the book should introduce such macro this early, but I couldn't avoid feeling bothered by not using the `nif` macro, which could even remove the need for the intermediate `test-value` variable:
+
+```scheme
+(define (search f neg-point pos-point)
+ (let ((midpoint (average neg-point pos-point)))
+ (if (close-enough? neg-point post-point)
+ midpoint
+ (nif (f midpoint)
+ (search f neg-point midpoint)
+ (midpoint)
+ (search f midpoint pos-point)))))
+```
+
+It also avoids `cond`'s extra clunky parentheses for grouping, which is unnecessary but built-in.
+
+As a macro, I personally feel it tilts the balance towards expressivenes despite its extra cognitive load toll.
+
+[on-lisp]: http://www.paulgraham.com/onlisptext.html
+[sicp]: https://mitpress.mit.edu/sites/default/files/sicp/index.html
diff --git a/v2/src/development/config.env.in b/v2/src/development/config.env.in
deleted file mode 100644
index ca2afe7..0000000
--- a/v2/src/development/config.env.in
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-
-SITE_NAME="EuAndreh's website"
-FQDN='@FQDN@'
-BASE_URL='@BASE_URL@'
-DATE_FMT='+%B %-d, %Y'
diff --git a/v2/src/development/dynmake.sh b/v2/src/development/dynmake.sh
deleted file mode 100755
index 126c211..0000000
--- a/v2/src/development/dynmake.sh
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/bin/sh
-set -eu
-
-
-usage() {
- cat <<-'EOF'
- Usage:
- dynmake.sh
- dynmake.sh -h
- EOF
-}
-
-help() {
- cat <<-'EOF'
-
- Options:
- -h, --help show this message
-
-
- Generate make(1) code for later evaluation by make(1). What
- this scripts does is fill the gap where make(1) can't handle
- globs and dynamic dependencies, and uses some ad-hoc scripts
- to generate those.
- 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))
-
-
-
-varlist() {
- sed -e 's/^/ /' \
- -e 's/$/ \\/'
-}
-
-
-#
-# Pastebins
-#
-
-pastebins() {
- find src/content/pastebins/ -name '*.md'
-}
-
-printf 'pastebins.md = \\\n'
-pastebins | varlist
-printf '\n'
-
-pastebins | sed 's/^\(.*\)\.md$/\1.html: \1.entry-env/'
-pastebins | sed 's/^\(.*\)\.md$/\1.html: \1.entry-content/'
-pastebins | sed 's/^\(.*\)\.md$/\1.entry-content: \1.entry-env/'
diff --git a/v2/src/development/genhtml.sh b/v2/src/development/genhtml.sh
index a1e8afb..cb8fd73 100755
--- a/v2/src/development/genhtml.sh
+++ b/v2/src/development/genhtml.sh
@@ -1,21 +1,6 @@
#!/bin/sh
set -eu
-# FIXMEs:
-# - feeds
-# - link to next and/or previous in <head>
-# - translation support
-# - validate input variables: regex for date (same as _plugins/linter.rb)
-# - `date -d` isn't POSIX
-# - parse commonmark and use a custom HTML emitter over <pre><code> regex
-# - handle mixture of personal scripts
-# - sitemap? How does it even work?
-# - dark mode
-# - generate security.txt dynamically
-# - config.env should depend on dynamic.mk?
-
-
-
usage() {
cat <<-'EOF'
Usage:
@@ -81,12 +66,13 @@ while getopts 'h' flag; do
done
shift $((OPTIND - 1))
+. src/development/lib.sh
FILENAME="${1:-}"
-eval "$(assert-arg "$FILENAME" 'FILENAME')"
+eval "$(assert_arg "$FILENAME" 'FILENAME')"
-. "${FILENAME%.md}.entry-env"
+. "${FILENAME%.md}.conf"
#
# Utility functions
@@ -216,28 +202,28 @@ emit_body() {
cat <<-EOF
<!DOCTYPE html>
- <html lang="$LANGUAGE">
+ <html lang="$lang">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
- <link rel="stylesheet" type="text/css" href="$(url-for 'static/styles.css')" />
- <link rel="icon" type="image/svg+xml" href="$(url-for 'static/favicon.svg')" />
+ <link rel="stylesheet" type="text/css" href="$style_url" />
+ <link rel="icon" type="image/svg+xml" href="$favicon_url" />
- <title>$TITLE</title>
+ <title>$$title_html</title>
<meta name="author" content="EuAndreh" />
- <meta property="og:site_name" content="$SITE_NAME" />
- <meta property="og:locale" content="$LANGUAGE" />
- <meta property="og:title" content="$TITLE" />
+ <meta property="og:site_name" content="$site_name" />
+ <meta property="og:locale" content="$lang" />
+ <meta property="og:title" content="$title_html" />
- <link rel="canonical" href="$(url-for "$URL" | absolute)" />
- <meta property="og:url" content="$(url-for "$URL" | absolute)" />
+ <link rel="canonical" href="$url" />
+ <meta property="og:url" content="$url" />
</head>
<body>
<header>
<nav>
<ul>
- <a href="$(url-for "$LANGUAGE/")">EuAndreh</a>
+ <a href="$(url-for "$lang/")">EuAndreh</a>
<a href="$(url-for 'about.html')">About</a>
</ul>
</nav>
@@ -245,8 +231,19 @@ cat <<-EOF
</header>
<main>
<article>
- $(emit_body)
+ \$(emit_body)
<hr />
+EOF
+exit
+
+
+.md.rehtml:
+ F="$<"; . "$${F%.md}.conf"; envsubst < src/lib/reamble."$$lang".html > $@
+
+.md.osthtml:
+ F="$<"; . "$${F%.md}.conf"; envsubst < src/lib/ostamble."$$lang".html > $@
+
+
<p class="post-footer">
<a href="mailto:~euandreh/public-inbox@lists.sr.ht?Subject=Re%3A%20$URI_TITLE">Comment</a>
and see
@@ -261,7 +258,7 @@ cat <<-EOF
<ul>
<li>
<img class="svg-icon" src="$(url-for 'static/envelope.svg')" alt="a envelope icon representing an email address" />
- <a href="mailto:eu@euandre.org">eu@euandre.org</a>
+ <a href="mailto:$email">$email</a>
</li>
<li>
<img class="svg-icon" src="$(url-for 'static/lock.svg')" alt="a lock icon representing a GPG public key" />
@@ -275,3 +272,16 @@ cat <<-EOF
</body>
</html>
EOF
+
+# FIXMEs:
+# - feeds
+# - link to next and/or previous in <head>
+# - translation support
+# - validate input variables: regex for date (same as _plugins/linter.rb)
+# - `date -d` isn't POSIX
+# - parse commonmark and use a custom HTML emitter over <pre><code> regex
+# - handle mixture of personal scripts
+# - sitemap? How does it even work?
+# - dark mode
+# - generate security.txt dynamically
+# - config.env should depend on dynamic.mk?
diff --git a/v2/src/development/getconf.sh b/v2/src/development/getconf.sh
new file mode 100755
index 0000000..dd623f7
--- /dev/null
+++ b/v2/src/development/getconf.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+set -eu
+
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ src/development/getconf.sh FILENAME
+ src/development/getconf.sh -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help show this message
+
+ FILENAME the name of the input file, also to be used as
+ URL.
+
+
+ Separate the content from the "frontmatter", and emit the
+ selected one, given the FILENAME.
+
+
+ Examples:
+
+ Get the "frontmatter" of src/f.conf:
+
+ $ src/development/getconf.sh src/f.md > src/f.conf
+ 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))
+
+. src/development/lib.sh
+
+FILENAME="${1:-}"
+eval "$(assert_arg "$FILENAME" 'FILENAME')"
+
+
+escape() {
+ sed 's|\([`"$]\)|\\\1|g'
+}
+
+{
+ cat src/lib/base-conf | tee "$FILENAME".tmp
+ DELIMITER=0
+ while read -r line; do
+ if [ "$line" = '---' ]; then
+ DELIMITER=$((DELIMITER + 1))
+ continue
+ fi
+ if [ "$DELIMITER" = 2 ]; then
+ break
+ fi
+ if [ -z "$line" ]; then
+ continue
+ fi
+
+ KEY="$( printf '%s' "$line" | cut -d: -f1)"
+ VALUE="$(printf '%s' "$line" | cut -d: -f2- | sed 's|^ ||' | escape)"
+ printf '%s="%s"\n' "$KEY" "$VALUE"
+ done < "$FILENAME" | tee -a "$FILENAME".tmp
+ . "$FILENAME".tmp
+
+ cat src/lib/base."$lang".conf
+ . src/lib/base."$lang".conf
+
+ title="${title:-"$site_name"}"
+ url_part="$(printf '%s' "${FILENAME%.md}.html" | sed 's|^src/content/||')"
+
+ printf 'title="%s"\n' "$(printf '%s' "$title" | escape)"
+ printf 'title_html="%s"\n' "$(printf '%s' "$title" | htmlesc | escape)"
+ printf 'filename="%s"\n' "$FILENAME"
+ printf 'url_part="%s"\n' "$url_part"
+ printf 'url="%s"\n' "$(url-for "$url_part" | absolute)"
+ printf 'date_formatted="%s"\n' "$(LANG="$lang" date -d "$date" +"$date_fmt" | escape)"
+ printf 'mailto_uri="%s%s"\n' "$mailto_uri_prefix" "$(uri "$title")"
+ printf 'discussions_url="%s%s"\n' "$discussions_url_prefix" "$(uri "$title")"
+ printf 'sourcecode_url="%s%s"\n' "$sourcecode_url_prefix" "$FILENAME"
+
+ printf 'lang_url="%s"\n' "$(url-for "$lang"/)"
+
+ printf 'style_url="%s"\n' "$(url-for 'style.css')"
+ printf 'favicon_url="%s"\n' "$(url-for 'favicon.svg')"
+
+ rm -f "$FILENAME".tmp
+} | grep . | sed 's|^|export |'
diff --git a/v2/src/development/lib.sh b/v2/src/development/lib.sh
new file mode 100644
index 0000000..9d183f9
--- /dev/null
+++ b/v2/src/development/lib.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+assert_arg() {
+ if [ -z "$1" ]; then
+ printf 'Missing %s.\n\n' "$2" >&2
+ cat <<-'EOF'
+ usage >&2
+ exit 2
+ EOF
+ fi
+}
+
+uuid() {
+ od -xN20 /dev/urandom |
+ head -n1 |
+ awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}'
+}
+
+tmpname() {
+ echo "${TMPDIR:-/tmp}/uuid-tmpname with spaces.$(uuid)"
+}
+
+mkstemp() {
+ name="$(tmpname)"
+ touch "$name"
+ echo "$name"
+}
+
+mkdtemp() {
+ name="$(tmpname)"
+ mkdir "$name"
+ echo "$name"
+}
diff --git a/v2/src/development/security-txt.sh.in b/v2/src/development/security-txt.sh.in
new file mode 100755
index 0000000..8f6613f
--- /dev/null
+++ b/v2/src/development/security-txt.sh.in
@@ -0,0 +1,82 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ src/development/security-txt.sh
+ src/development/security-txt.sh -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+
+ Options:
+ -h, --help show this message
+
+
+ Generate the RFC 9116 "security.txt" file from data in the
+ repository.
+
+
+ Examples:
+
+ Just run it:
+
+ $ sh src/development/security-txt.sh > .well-known/security.txt
+ 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))
+
+
+
+EXPIRES="$(
+ LANG=C.UTF-8 gpg --list-key eu@euandre.org |
+ awk '/^pub/ { print substr($(NF), 1, 10) }'
+)T00:00:00z"
+
+LANGS="en$(
+ echo po/??.po |
+ sed 's|\.po$||' |
+ sed 's|^po/|, |' |
+ paste -sd,
+)"
+
+
+cat <<-EOF
+ Contact: mailto:@EMAIL@
+ Encryption: https://@DOMAIN@/public.asc.txt
+ Expires: $EXPIRES
+ Preferred-Languages: $LANGS
+EOF
diff --git a/v2/src/lib/base-conf.in b/v2/src/lib/base-conf.in
new file mode 100644
index 0000000..a33755a
--- /dev/null
+++ b/v2/src/lib/base-conf.in
@@ -0,0 +1,9 @@
+site_name="EuAndreh's website"
+domain='@DOMAIN@'
+email='@EMAIL@'
+base_url=''
+lang=en
+list_addr='~euandreh/public-inbox@lists.sr.ht'
+mailto_uri_prefix="mailto:$list_addr?Subject=Re%3A%20"
+discussions_url_prefix="https://lists.sr.ht/~euandreh/public-inbox?search="
+sourcecode_url_prefix="https://$domain/git/$domain/tree/"
diff --git a/v2/src/lib/base.en.conf b/v2/src/lib/base.en.conf
new file mode 100644
index 0000000..89344fb
--- /dev/null
+++ b/v2/src/lib/base.en.conf
@@ -0,0 +1,3 @@
+date_fmt='%B %-d, %Y'
+
+site_name="EuAndreh's website"
diff --git a/v2/src/lib/base.pt.conf b/v2/src/lib/base.pt.conf
new file mode 100644
index 0000000..89344fb
--- /dev/null
+++ b/v2/src/lib/base.pt.conf
@@ -0,0 +1,3 @@
+date_fmt='%B %-d, %Y'
+
+site_name="EuAndreh's website"
diff --git a/v2/src/lib/postamble.en.html b/v2/src/lib/postamble.en.html
new file mode 100644
index 0000000..485d81c
--- /dev/null
+++ b/v2/src/lib/postamble.en.html
@@ -0,0 +1,7 @@
+<p>
+ <a href="@mailto_uri@">Comment</a>
+ and see
+ <a href="@discussions_url@">existing discussions</a>
+ |
+ <a href="@sourcecode_url@">view source</a>
+</p>
diff --git a/v2/src/lib/postamble.pt.html b/v2/src/lib/postamble.pt.html
new file mode 100644
index 0000000..2e65735
--- /dev/null
+++ b/v2/src/lib/postamble.pt.html
@@ -0,0 +1,5 @@
+<p>
+ <a href="@mailto_uri@">Comment</a> and see <a
+href="@discussions_url@">existing discussions</a> | <a
+href="@sourcecode_url@">view source</a>
+</p>
diff --git a/v2/src/lib/preamble.en.html b/v2/src/lib/preamble.en.html
new file mode 100644
index 0000000..9ea6780
--- /dev/null
+++ b/v2/src/lib/preamble.en.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="$lang">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="stylesheet" type="text/css" href="$style_url" />
+ <link rel="icon" type="image/svg+xml" href="$favicon_url" />
+
+ <title>$(htmlesc "$title")</title>
+
+ <meta name="author" content="EuAndreh" />
+ <meta property="og:site_name" content="$site_name" />
+ <meta property="og:locale" content="$lang" />
+ <meta property="og:title" content="$title_html" />
+
+ <link rel="canonical" href="$url" />
+ <meta property="og:url" content="$url" />
+ </head>
+ <body>
+ <header>
+ <nav>
+ <ul>
+ <a href="$lang_url">EuAndreh</a>
+ <a href="$(url-for 'about.html')">About</a>
+ </ul>
+ </nav>
+ <hr />
+ </header>
+ <main>
+ <article>
+ <h1>
+ $title_html
+ </h1>
+ <p class="timestamp">
+ Posted on <time datetime="$date">$date_formatted</time>
+ </p>
+EOF
diff --git a/v2/src/lib/preamble.pt.html b/v2/src/lib/preamble.pt.html
new file mode 100644
index 0000000..9800470
--- /dev/null
+++ b/v2/src/lib/preamble.pt.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="$lang">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="stylesheet" type="text/css" href="$style_url" />
+ <link rel="icon" type="image/svg+xml" href="$favicon_url" />
+
+ <title>$(htmlesc "$title")</title>
+
+ <meta name="author" content="EuAndreh" />
+ <meta property="og:site_name" content="$site_name" />
+ <meta property="og:locale" content="$lang" />
+ <meta property="og:title" content="$title_html" />
+
+ <link rel="canonical" href="$url" />
+ <meta property="og:url" content="$url" />
+ </head>
+ <body>
+ <header>
+ <nav>
+ <ul>
+ <a href="$lang_url">EuAndreh</a> <a href="$(url-for 'about.html')">About</a>
+ </ul>
+ </nav>
+ <hr />
+ </header>
+ <main>
+ <article>
+ <h1>
+ $title_html
+ </h1>
+ <p class="timestamp">
+ Posted on <time datetime="$date">$date_formatted</time>
+ </p>
+EOF