aboutsummaryrefslogtreecommitdiff
path: root/v2
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2023-04-04 08:33:41 -0300
committerEuAndreh <eu@euandre.org>2023-04-04 08:43:15 -0300
commit08588f9907299b1a927e281d5c65b46b7cefa427 (patch)
tree860f8550c2efee35df9bfa1ef56e338f8331c2d1 /v2
parentdynamic.mk: Use serve(1) as is (diff)
downloadeuandre.org-08588f9907299b1a927e281d5c65b46b7cefa427.tar.gz
euandre.org-08588f9907299b1a927e281d5c65b46b7cefa427.tar.xz
Revamp v2/
Diffstat (limited to 'v2')
-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
48 files changed, 1523 insertions, 326 deletions
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