Wren Linux

wren-linux / tools / build-site.sh

#!/bin/bash
# build-site.sh: makes Wren Linux's website, a folder of plain files, from one
# commit: its pages (site/), and a browsable copy of every file in it, with the
# commit log and a download of the whole commit. No JavaScript, no cookies,
# nothing loaded from elsewhere: it works in Tor Browser's Safest mode. The
# signing key's fingerprint and the git address come from pins.sh.
#  - --out DIR: where the site goes. --commit REV: the commit it is built from
#    (default: HEAD, this copy's newest).
#  - --require-signed (what tools/publish-site.sh uses): only a commit signed
#    by the key pins.sh pins, checked as update.sh checks one, so the site
#    shows exactly what that key vouched for.
#  - --onion ADDRESS: the site's onion address, shown on every page and on
#    Verify (setup-vps.sh prints it).
#  - --pages DIR: the pages from DIR instead of the commit's site/ (to try
#    changes before they are committed; never with --require-signed).
#  - --preview: the pages as they are in this copy (site/, committed or not),
#    into ~/Downloads/wren-site-preview (unless --out says otherwise), opened in
#    the sandboxed Firefox: it may read ~/Downloads, so nothing listens on a
#    port and no firewall opens. (The server's headers aren't there: look at
#    those on the test VM, tools/try-vps.sh.)
# Usage: bash tools/build-site.sh --out DIR [--commit REV] [--onion ADDRESS] [--require-signed] [--pages DIR]
#        bash tools/build-site.sh --preview [--out DIR]
. "$(dirname -- "${BASH_SOURCE[0]}")/../lib.sh"
. "$WREN_DIR/lib/appimage.sh"
. "$WREN_DIR/lib/update.sh"
not_root

g() { git -C "$WREN_DIR" "$@"; }
usage() { die "Usage: bash tools/build-site.sh --out DIR [--commit REV] [--onion ADDRESS] [--require-signed] [--pages DIR]"; }

out='' rev=HEAD onion='' signed=false pages='' preview=false
while [ $# -gt 0 ]; do
    case $1 in
        --out) out=${2:-}; shift 2 || usage ;;
        --commit) rev=${2:-}; shift 2 || usage ;;
        --onion) onion=${2:-}; shift 2 || usage ;;
        --pages) pages=${2:-}; shift 2 || usage ;;
        --require-signed) signed=true; shift ;;
        --preview) preview=true; shift ;;
        *) usage ;;
    esac
done
if $preview; then
    $signed && die "--preview shows what isn't committed yet, so not with --require-signed."
    [ -n "$pages" ] || pages=$WREN_DIR/site
    [ -n "$out" ] || out=$HOME/Downloads/wren-site-preview
fi
[ -n "$out" ] || usage
# It replaces OUT: only a folder it made before (its mark inside), or none
if [ -e "$out" ] && [ ! -f "$out/.wren-site" ]; then
    die "$out exists and isn't a site this made (no .wren-site in it); give a new folder."
fi
[ -z "$onion" ] || [[ $onion =~ ^[a-z2-7]{56}\.onion$ ]] || die "\"$onion\" isn't an onion address (56 letters and digits, then .onion)."
if $signed && [ -n "$pages" ]; then die "--pages takes pages that aren't committed, so not with --require-signed."; fi
need_tools git:git python3:python3

commit=$(g rev-parse --verify --quiet "$rev^{commit}") || die "\"$rev\" isn't a commit here."
if $signed; then
    [ -n "${WREN_SIGNING_FPR:-}" ] || die "pins.sh pins no signing key, so nothing can be checked."
    update_signed "$commit" || die "Commit $commit isn't signed by Wren Linux's key ($WREN_SIGNING_FPR); the site wasn't built."
fi
# What the site says comes from the commit too: its pins.sh, not this copy's
fpr=$(g show "$commit:pins.sh" | sed -n 's/^WREN_SIGNING_FPR=//p' | head -n1)
url=$(g show "$commit:pins.sh" | sed -n 's/^WREN_GIT_URL=//p' | head -n1)
[[ $fpr =~ ^[0-9A-F]{40}$ ]] || die "The commit's pins.sh pins no signing key."

T=$(mktemp -d)
trap 'rm -rf "$T"' EXIT
mkdir "$T/src"
g archive --format=tar "$commit" | tar -C "$T/src" -xf -
[ -n "$pages" ] || pages=$T/src/site
[ -f "$pages/layout.html" ] || die "$pages has no layout.html: is it the site's folder?"
g log -n 60 --format='%H%x09%ad%x09%s' --date=short "$commit" >"$T/log"

rm -rf "$out.new"
mkdir -p "$out.new"
SITE_FPR=$fpr SITE_GIT_URL=$url SITE_COMMIT=$commit SITE_ONION=$onion SITE_SIGNED=$($signed && echo yes) \
    python3 -I "$WREN_DIR/tools/site.py" "$pages" "$T/src" "$T/log" "$out.new" || die "The site couldn't be made."
# The whole commit as one download, the same bytes each time it is made
g archive --format=tar --prefix=wren-linux/ "$commit" | gzip -9n >"$out.new/wren-linux.tar.gz"
(cd "$out.new" && sha256sum wren-linux.tar.gz >wren-linux.tar.gz.sha256)
echo "$commit" >"$out.new/.wren-site"
# Every file readable by the web server, nothing executable
find "$out.new" -type d -exec chmod 0755 {} + && find "$out.new" -type f -exec chmod 0644 {} +
rm -rf "$out"
mv "$out.new" "$out"
note "The site, from commit ${commit:0:12}, is in $out ($(du -sh "$out" | cut -f1))."
if $preview; then
    # (a test gives its own pretend firefox)
    fx=${WREN_FIREFOX:-/usr/local/bin/firefox}
    if [ -x "$fx" ]; then
        note "Opening it in the sandboxed Firefox."
        "$fx" "file://$out/index.html" >/dev/null 2>&1 &
    else
        note "Open it in a browser: file://$out/index.html"
    fi
fi