From dc0250b60d1dbf8bf8d3e04e05447316b49884c4 Mon Sep 17 00:00:00 2001 From: Danny O'Brien Date: Wed, 13 Dec 2023 00:10:12 -0800 Subject: [PATCH] doc: Add basic docs, and link tests --- Makefile | 11 ++ bin/tapview | 283 ++++++++++++++++++++++++++++++++++++++ doc/index.org | 10 ++ doc/site-design.org | 25 ++++ doc/site-operation.org | 8 ++ doc/style-guide.org | 3 + src/tests/README.md | 3 + src/tests/doc-check-links | 44 ++++++ 8 files changed, 387 insertions(+) create mode 100755 bin/tapview create mode 100644 doc/index.org create mode 100644 doc/site-design.org create mode 100644 doc/site-operation.org create mode 100644 doc/style-guide.org create mode 100644 src/tests/README.md create mode 100755 src/tests/doc-check-links diff --git a/Makefile b/Makefile index e22cbf4..2c9ea7a 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,8 @@ SRC_DIR := $(ALMANACK_ROOT)/web ALMANACK_ROOT := $(ALMANACK_ROOT) +# +# # Define the destination for rsync DEST := boat:/var/local/www/www.almnck.com/ @@ -21,3 +23,12 @@ ifndef ALMANACK_ROOT $(error ALMANACK_ROOT is undefined. Please export it before running 'make live'.) endif @rsync $(RSYNC_OPTS) $(SRC_DIR) $(DEST) + +.PHONY: test-doc +test-doc: +ifndef ALMANACK_ROOT + $(error ALMANACK_ROOT is undefined. Please export it before running 'make test-doc'.) +endif + @echo Running doc-check-links + @sh $(ALMANACK_ROOT)/src/tests/doc-check-links | tapview + diff --git a/bin/tapview b/bin/tapview new file mode 100755 index 0000000..5a76741 --- /dev/null +++ b/bin/tapview @@ -0,0 +1,283 @@ +#! /bin/sh +# tapview - a TAP (Test Anything Protocol) viewer in pure POSIX shell +# +# SPDX-FileCopyrightText: Eric S. Raymond +# SPDX-License-Identifier: MIT-0 +# +# This code is intended to be embedded in your project. The author +# grants permission for it to be distributed under the prevailing +# license of your project if you choose, provided that license is +# OSD-compliant; otherwise the following SPDX tag incorporates the +# MIT No Attribution license by reference. +# +# A newer version may be available at https://gitlab.com/esr/tapview +# Check your last commit dqte for this file against the commit list +# there to see if it might be a good idea to update. +# +OK="." +FAIL="F" +SKIP="s" +TODO_NOT_OK="x" +TODO_OK="u" + +LF=' +' + +ship_char() { + # shellcheck disable=SC2039 + printf '%s' "$1" # https://www.etalabs.net/sh_tricks.html +} + +ship_line() { + report="${report}${1}$LF" +} + +ship_error() { + # Terminate dot display and bail out with error + if [ "${testcount}" -gt 0 ] + then + echo "" + fi + report="${report}${1}$LF" + echo "${report}" + exit 1 +} + +testcount=0 +failcount=0 +skipcount=0 +todocount=0 +status=0 +report="" +IFS="" +ln=0 +state=plaintext + +# shellcheck disable=SC2086 +context_get () { printenv "ctx_${1}${depth}"; } +context_set () { export "ctx_${1}${depth}=${2}"; } + +context_push () { + context_set plan "" + context_set count 0 + context_set test_before_plan no + context_set test_after_plan no + context_set expect "" + context_set bail no + context_set strict no +} + +context_pop () { + if [ "$(context_get count)" -gt 0 ] && [ -z "$(context_get plan)" ] + then + ship_line "Missing a plan at line ${ln}." + status=1 + elif [ "$(context_get test_before_plan)" = "yes" ] && [ "$(context_get test_after_plan)" = "yes" ] + then + ship_line "A plan line may only be placed before or after all tests." + status=1 + elif [ "$(context_get plan)" != "" ] && [ "$(context_get expect)" -gt "$(context_get count)" ] + then + ship_line "Expected $(context_get expect) tests but only ${testcount} ran." + status=1 + fi +} + +depth=0 +context_push + +while read -r line +do + ln=$((ln + 1)) + # Process bailout + if expr "$line" : "Bail out!" >/dev/null + then + ship_line "$line" + status=2 + break + fi + # Use the current indent to choose a scope level + indent=$(expr "$line" : '[ ]*') + if [ "${indent}" -lt "${depth}" ] + then + context_pop + depth="${indent}" + elif [ "${indent}" -gt "${depth}" ] + then + depth="${indent}" + context_push + fi + # Process a plan line + if expr "$line" : '[ ]*1\.\.[0-9][0-9]*' >/dev/null + then + if [ "$(context_get plan)" != "" ] + then + ship_error "tapview: cannot have more than one plan line." + fi + if expr "$line" : ".* *SKIP" >/dev/null || expr "$line" : ".* *skip" >/dev/null + then + ship_line "$line" + echo "${report}" + exit 1 # Not specified in the standard whether this should exit 1 or 0 + fi + context_set plan "${line}" + context_set expect "$(expr "$line" : '[ ]*1\.\.\([0-9][0-9]*\)')" + continue + elif expr "$line" : '[ ]*[0-9][0-9]*\.\.[0-9][0-9]*' >/dev/null + then + echo "Ill-formed plan line at ${ln}" + exit 1 + fi + # Check for out-of-order test point numbers with the sequence (TAP 14) + testpoint=$(expr "$line" : '.*ok *\([0-9][0-9]*\)') + if [ "${testpoint}" != "" ] && [ "$(context_get expect)" != "" ] && [ "${testpoint}" -gt "$(context_get expect)" ] + then + ship_error "tapview: testpoint number ${testpoint} is out of range for plan $(context_get plan)." + fi + # Process an ok line + if expr "$line" : "[ ]*ok" >/dev/null + then + context_set count $(($(context_get count) + 1)) + testcount=$((testcount + 1)) + if [ "$(context_get plan)" = "" ] + then + context_set test_before_plan yes + else + context_set test_after_plan yes + fi + if expr "$line" : "[^#]* # *TODO" >/dev/null || expr "$line" : "[^#]* # *todo" >/dev/null + then + ship_char ${TODO_OK} + ship_line "$line" + todocount=$((todocount + 1)) + if expr "$line" : "[^#]*#[^ ]" >/dev/null + then + ship_line "Suspicious comment leader at ${ln}" + fi + elif expr "$line" : "[^#]* # *SKIP" >/dev/null || expr "$line" : "[^#]* # *skip" >/dev/null + then + ship_char ${SKIP} + ship_line "$line" + skipcount=$((skipcount + 1)) + if expr "$line" : "[^#]*#[^ ]" >/dev/null + then + ship_line "Suspicious comment leader at ${ln}" + fi + else + ship_char ${OK} + fi + state=plaintext + continue + fi + # Process a not-ok line + if expr "$line" : "[ ]*not ok" >/dev/null + then + context_set count $(($(context_get count) + 1)) + testcount=$((testcount + 1)) + if [ "$(context_get plan)" = "" ] + then + context_set test_before_plan yes + else + context_set test_after_plan yes + fi + if expr "$line" : "[^#]* # *SKIP" >/dev/null || expr "$line" : "[^#]* # *skip" >/dev/null + then + ship_char "${SKIP}" + state=plaintext + skipcount=$((skipcount + 1)) + if expr "$line" : "[^#]* #[^ ]" >/dev/null + then + ship_line "Suspicious comment leader at lime ${ln}" + fi + continue + fi + if expr "$line" : "[^#]* # *TODO" >/dev/null || expr "$line" : "[^#]* # *todo" >/dev/null + then + ship_char ${TODO_NOT_OK} + state=plaintext + todocount=$((todocount + 1)) + if expr "$line" : "[^#]* #[^ ]" >/dev/null + then + ship_line "Suspicious comment leader at line ${ln}" + fi + continue + fi + ship_char "${FAIL}" + ship_line "$line" + state=plaintext + failcount=$((failcount + 1)) + status=1 + if [ "$(context_get bail)" = yes ] + then + ship_line "Bailing out on line ${ln} due to +bail pragma." + break + fi + continue + fi + # Process a TAP 14 pragma + if expr "$line" : "pragma" >/dev/null + then + unset IFS + # shellcheck disable=SC2086 + set -- $line + case "$2" in + +bail) context_set bail yes;; + -bail) context_set bail yes;; + +strict) context_set strict yes;; + -strict) context_set strict yes;; + *) ship_line "Pragma '$line' ignored";; + esac + IFS="" + continue + fi + # shellcheck disable=SC2166 + if [ "${state}" = "yaml" ] + then + ship_line "$line" + if expr "$line" : '[ ]*\.\.\.' >/dev/null + then + state=plaintext + else + continue + fi + elif expr "$line" : "[ ]*---" >/dev/null + then + ship_line "$line" + state=yaml + continue + fi + # Ignore blank lines and comments + if [ -z "$line" ] || expr "$line" : '[ ]+$' >/dev/null || expr "$line" : "#" >/dev/null + then + continue + fi + # Any line that is not a valid plan, test result, pragma, + # or comment lands here. + if [ "$(context_get strict)" = yes ] + then + ship_line "Bailing out on line ${ln} due to +strict pragma" + status=1 + break + fi +done + +/bin/echo "" + +depth=0 +context_pop + +report="${report}${testcount} tests, ${failcount} failures" +if [ "$todocount" != 0 ] +then + report="${report}, ${todocount} TODOs" +fi +if [ "$skipcount" != 0 ] +then + report="${report}, ${skipcount} SKIPs" +fi + +echo "${report}." + +exit "${status}" + +# end diff --git a/doc/index.org b/doc/index.org new file mode 100644 index 0000000..5ffffb8 --- /dev/null +++ b/doc/index.org @@ -0,0 +1,10 @@ +#+title: Your Petite Guide to Almanack + +* Corporate Structure and Finances :CORPORATE: +* Website :WEBSITE: + [[./site-design.org][Site Design]] + [[./site-operation.org][Site Operation]] +* Editorial Queue :EDITORIAL: +* Commissions and Invoicing :INVOICING: +* Style and Tone :STYLE: + [[./style-guide.org][Style Guide]] diff --git a/doc/site-design.org b/doc/site-design.org new file mode 100644 index 0000000..1fbf3c1 --- /dev/null +++ b/doc/site-design.org @@ -0,0 +1,25 @@ +* Site Production +:PROPERTIES: +:CUSTOM_ID: site-production +:END: +The Almanack site is managed by Django, but the production version is +/static content/, generated by +[[https://django-distill.com/][django-distill]]. Interactivity is +provided by [[https://htmx.org/][htmx]], and the HTML that is loaded on +demand by that javascript library is, as much as possible, stored as +HTML fragments in individual files, generate as part of the same +distillation process. + +This dynamically-generated, but staticly-presented model allows us to +host the site using IPFS as well as over HTTPS. It also allows us to +pursue a local-first model, as the site can be passively loaded and +aggressively cached in the background. + +* User Authentication +:PROPERTIES: +:CUSTOM_ID: user-authentication +:END: +For authentication purposes, we associate public keys with each user +account and only support authentication systems that allow us to only +store public keys. This is so we don't have to faff around protecting +passwords, and also to walk our walk. diff --git a/doc/site-operation.org b/doc/site-operation.org new file mode 100644 index 0000000..6b7e972 --- /dev/null +++ b/doc/site-operation.org @@ -0,0 +1,8 @@ + +# Updating to Live Site +```sh +cd $ALMANACK_ROOT +make live +```sh + +This rsyncs the local web root to boat, which currently hosts the Almanack website. diff --git a/doc/style-guide.org b/doc/style-guide.org new file mode 100644 index 0000000..d2a391d --- /dev/null +++ b/doc/style-guide.org @@ -0,0 +1,3 @@ + +* Overarching goal: + Our readers should be more energised after reading a serving of Almanack than they were before. diff --git a/src/tests/README.md b/src/tests/README.md new file mode 100644 index 0000000..782ca9d --- /dev/null +++ b/src/tests/README.md @@ -0,0 +1,3 @@ +This folder for ALL tests! + +Tests should return their results in [Test Anything Protocol](https://testanything.org/). diff --git a/src/tests/doc-check-links b/src/tests/doc-check-links new file mode 100755 index 0000000..0112c22 --- /dev/null +++ b/src/tests/doc-check-links @@ -0,0 +1,44 @@ +#!/bin/sh +### +# Checks orgfiles in doc for bad links +### + +# Converts .org files into HTML, then runs a link checker with TAP output + +echo "TAP version 13" + +if [ -z "$ALMANACK_ROOT" ] || ! cd "$ALMANACK_ROOT/doc/"; then + echo "Could not change directory to $ALMANACK_ROOT/doc/" + exit 1 +fi + +# Count .org files for the test plan +orgfiles_count=$(find . -name '*.org' -type f -exec echo X \; | wc -l | tr -d ' ') + +# Check if there are any .org files +if [ "$orgfiles_count" -eq "0" ]; then + echo "No .org files found." + exit 0 +fi + +# Output the test plan (total number of org files/tests) +echo "1..$orgfiles_count" + +# Initialize test counter +test_counter=1 + +find . -name '*.org' -type f | while IFS= read -r orgfile; do + orgfile_basename=$(basename "$orgfile") + if pandoc "$orgfile" -t html -o "test.html" && linkchecker --no-warnings -q "test.html"; then + echo "ok $test_counter - $orgfile_basename link check" + else + echo "not ok $test_counter - $orgfile_basename link check" + linkchecker --no-warnings "test.html" 2>&1 | sed 's/^/# /g' + fi + + # Cleaning up the test file + rm "test.html" + + # Increment the test number + test_counter=$(expr $test_counter + 1) +done