#!/bin/bash #shellcheck disable=SC1090 . "$(sfpath)" || exit 3 shellfu import pretty usage() { mkusage "$@" \ "[options] SRC DST" \ -- \ "Copy tree from SRC to DST, converting Markdown to HTML pages." \ "" \ -o \ "-d, --debug Turn on debbugging output" \ "--log LOG Store publish plan in LOG file. (Keep this" \ " file PRIVATE!)" \ "--if-ready Publish only if .mdpublish/ready file exists;" \ " delete it afterwards (useful for cron jobs)" \ "-f, --force Remove all content from DST beforehand" \ "-v, --verbose Be more verbose" \ "--version Show version" \ -- \ "By default, mdpublish does not keep logfile. If you provide" \ "one, make sure it's outside the published content, otherwise" \ "someone might get list of all files on your site." } # # Markdown to HTML command # # Has to accept Markdown on standard input and pring HTML on standard # output. # MDPUBLISH__MDBIN=${MDPUBLISH__MDBIN:-markdown} # # Logfile for plan details # MDPUBLISH__LOG=${MDPUBLISH__LOG:-/dev/null} # # Style to load into each generated HTML page # MDPUBLISH__STYLE="_mdpublish/style.css" # # Nav to load for each generated HTML page # MDPUBLISH__NAV=".mdpublish/nav" nonempty() { # # True if directory $1 has files # local dir=$1 local es test -n "$dir" \ || warn "usage: nonempty DIR" test -d "$dir" \ || warn "usage: nonempty DIR" test -e "$dir"/* 2>/dev/null; es=$? case $es in 0) return 0 ;; # one file 2) return 0 ;; # more files (`test` usage error) *) return 1 ;; # anything else esac } mktitle() { # # Read title from Markdown file $MdSrc # local lines=() local head head="$(head -3 "$MdSrc")" readarray lines <<<"$head" debug -v head lines test -n "${lines[0]}" || return 1 grep -qx '==*' <<<"${lines[1]}" || return 1 test -n "${lines[2]}" && return 1 echo "${lines[0]}" return 0 } mkhead() { # # Print page head # echo '' echo '' echo ' ' echo " $Title" test -n "$Style" && echo " " echo ' ' echo ' ' echo ' ' echo '
' mknav echo '
' } mknav() { local self local text local href local self_id test -n "$Nav" || return 0 echo ' ' } mktail() { echo '
' echo '
' echo "published by mdpublish, $(date -I)" echo '
' echo '
' echo ' ' echo '' } conv1() { # # Convert Markdown file $1 to HTML on stdout # local MdSrc local Title MdSrc=$(mktemp -t mdpublish.XXXXXXX) cat > "$MdSrc" Title=$(mktitle) || Title="(untitled) $Relpath" debug -v Title mkhead <"$MdSrc" conv_links | "$MDPUBLISH__MDBIN" mktail rm "$MdSrc" } show_version() { echo "v0.0.0" echo "(mdpublish is not versioned yet)" } conv_links() { # # Convert Markdown links to HTML links # perl -pe ' m|^ \[[^]]+]: | or next; m|: https?:| and next; s|\.md$|.html|; ' } main() { local SrcTree local DstTree local force=false local Style local BaseUrl local Nav local ck_ready=false local PlanLog=$MDPUBLISH__LOG #shellcheck disable=SC2034 while true; do case "$1" in -b|--baseurl) BaseUrl=$2; shift 2 || usage ;; -f|--force) force=true; shift ;; -d|--debug) PRETTY_DEBUG=true; shift ;; --if-ready) ck_ready=true; shift ;; --log) PlanLog=$2; shift 2 || usage ;; -v|--verbose) PRETTY_VERBOSE=true; shift ;; --version) show_version; exit 0 ;; -*) usage ;; *) break ;; esac done SrcTree=$1; DstTree=$2 test -n "$SrcTree" || usage -w "no SRC?" test -n "$DstTree" || usage -w "no DST?" test -d "$SrcTree" || die "no such directory: $SrcTree" test -d "$DstTree" || die "no such directory: $DstTree" if $ck_ready; then if test -f "$SrcTree/.mdpublish/ready"; then rm "$SrcTree/.mdpublish/ready" \ && think "removed ready flag" else return 1 fi fi if nonempty "$DstTree"; then if $force; then think "cleaning up: $DstTree" #shellcheck disable=SC2115 rm -r "$DstTree/"* else die "destination has files (use -f to clean): $DstTree" fi fi test -f "$SrcTree/$MDPUBLISH__STYLE" && { think "style found: $SrcTree/$MDPUBLISH__STYLE" Style="$MDPUBLISH__STYLE" } test -f "$SrcTree/$MDPUBLISH__NAV" && { think "nav found: $SrcTree/$MDPUBLISH__NAV" Nav="$SrcTree/$MDPUBLISH__NAV" } debug -v SrcTree DstTree MDPUBLISH__STYLE MDPUBLISH__MDBIN mkplan \ | tee "$PlanLog" \ | exec_plan } mkplan() { # # Create publish plan from $SrcTree # # Output one line for each file to publish, in format # # static RELPATH # url RELPATH URL # # In case of 'static', source will be taken directly from $SrcTree. # In case if 'url', source file will be downloaded from URL. # In both cases, RELPATH determines relative path of final file # under destination tree. RELPATH is also what determines # whether Markdown conversion happens. # local urls=$SrcTree/.mdpublish/urls local relpath local url find -L "$SrcTree" -type f -printf 'static %P\n' \ | grep -v \ -e '^static [.]git$' \ -e '^static [.]git/.*' test -f "$urls" || return 0 while read -r relpath url; do echo "url $relpath $url" done <"$urls" } exec_plan() { # # Execute publish plan on stdin # local ptype local Relpath local Origin local genfn local dstpath while read -r ptype Relpath Origin; do case $ptype in static) genfn=gen_static ;; url) genfn=gen_url; ;; *) die "invalid publishing keyword: $ptype" ;; esac debug -v Relpath dstpath case $Relpath in _mdpublish/*) $genfn | publish_pipe "$Relpath" ;; *.md) think "converting: $Relpath" $genfn | conv1 | publish_pipe "${Relpath%.md}.html" ;; .*) think "skipping: $Relpath" ;; *) $genfn | publish_pipe "$Relpath" ;; esac done } gen_static() { # # Read static file $Relpath from $SrcTree # cat "$SrcTree/$Relpath" } gen_url() { # # Read file from URL $Origin # think "fetching: $Relpath from $Origin" curl -fsSL "$Origin" || die "failed to fetch URL: $Origin" } publish_pipe() { # # Publish content of stdin as $1 # local relpath=$1 local dstpath="$DstTree/$relpath" mkdir -p "$(dirname "$dstpath")" cat >"$dstpath" think "publishing: $relpath" } main "$@"