From c5ad7625aaaf32ba407729aa63b3d48edb5eb910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Gon=C3=A7alves?= Date: Wed, 15 May 2024 12:51:49 +0200 Subject: feat: init static site generator --- functions.sh | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ rssg | 48 +++++++++++ ssg | 29 +++++++ 3 files changed, 334 insertions(+) create mode 100644 functions.sh create mode 100755 rssg create mode 100755 ssg diff --git a/functions.sh b/functions.sh new file mode 100644 index 0000000..0468b29 --- /dev/null +++ b/functions.sh @@ -0,0 +1,257 @@ +#!/bin/sh + +media_dir="media" +src_dir="src" +tmp_dir="tmp" +out_dir="out" + +tmp_file="${tmp_dir}/tmp" +rss_dir="${src_dir}/b" +rss_out_file="${out_dir}/rss.xml" + +website_domain="rgoncalves.se" +website_title="${website_domain}" +website_link="https://${website_domain}" +website_email="contact@${website_domain}" +website_generator="${website_domain}'s ssg" +website_description="devops and hacker D:" +website_language="en" + +date_default_hour="17:00:00" +date_default_timezone="+0200" + +uname=$(uname) + +__sha256="sha256" + +if [ "${uname}" = "Linux" ]; then + __sha256="sha256sum" +fi + +__to_html_id() { + cat /dev/stdin | sed 's/ /%20/g' +} + +__get_value() { + # 1: filename + # 2: Attribute name + # Retrieve and return the key/value of a lowdown source file. + lowdown -T ms -X "${2}" "${1}" 2>/dev/null +} + +__get_value_title() { + # 1: filename + # Get the title of a file and return a safe string. + local _title + + _title="$(__get_value ${1} title || true)" + if [ "${_title}" = "" ]; then + _title="$(basename ${1} | + rev | + cut -d "." -f 2 | + rev | + sed 's/-/ /g')" + fi + + echo "${_title}" +} + +__get_value_date() { + # 1: filename + # Get the date of a file and return a safe string. + local _date + + _date="$(__get_value ${1} date || true)" + if [ "${_date}" = "" ]; then + _date="1970-01-01" + fi + + echo "${_date}" +} + +__get_value_date_human() { + # 1: filename + # Get a human readable date from a file and return a safe string. + if [ "${uname}" = "Linux" ]; then + __get_value_date "${1}" | xargs date +"%d %b %Y" -d + else + __get_value_date "${1}" | xargs date -j -f "%Y-%m-%d" +"%d %b %Y" + fi +} + +__get_value_date_publication() { + # 1: filename + # Get publication date according to rfc 2822 and return a safe string. + local _date + + _date="$(__get_value_date ${1})" + _date="${_date} ${date_default_hour} ${date_default_timezone}" + + if [ "${uname}" = "Linux" ]; then + date -d "${_date}" +"%a, %d %b %Y %H:%M:%S %z" + else + date -j -f "%Y-%m-%d %H:%M:%S %z" +"%a, %d %b %Y %H:%M:%S %z" "${_date}" + fi +} + +__get_out_filename() { + # 1: filename + # Convert the source filename to its output destination. + local _filename + + _filename="${1}" + + if $(__get_value "${1}" draft || false); then + _filename="$(dirname ${1})/$(basename ${1} | "${__sha256}").html" + fi + + echo "${_filename}" | + sed 's/.md$/.html/g' | + sed "s/${src_dir}/${out_dir}/g" +} + +__get_final_filename() { + # 1: filename + # Convert the source filename to its final filename for a webserver. + __get_out_filename "${1}" | cut -d "/" -f 2- +} + +__list_files() { + # 1: directory + # List all regular files in a directory and its subdirectories. + find "${1}" -type f +} + +__list_files_date() { + # 1: directory + local _files + local _file + local _tmp_file + + _files=$(__list_files "${1}") + _tmp_file="${tmp_file}.list_files_date" + + cp /dev/null "${_tmp_file}" + + for _file in ${_files}; do + echo "$(__get_value_date ${_file})" "${_file}" >> "${_tmp_file}" + done + + sort -r "${_tmp_file}" | cut -d " " -f 2- +} + +__install() { + # 1: filename + # Copy a file to the output directory. + install -D -m 0644 "${1}" "${2}" +} + +__lowdown() { + # Output a file to html + lowdown --html-no-skiphtml --html-no-escapehtml "${1}" +} + +__generate_rss_body() { + lowdown -tgemini "${1}" | sed 's/&/\&/g; s//\>/g; s/"/\"/g; s/'"'"'/\'/g' +} + +__apply_template() { + # 1: template name or default + m4 "templates/${1:-default}" +} + +__generate_index_line() { + # 1: filename where index will be displayed + # 2: filename contained in line + local _show_date + + _show_date="$(__get_value ${1} index_date || echo false)" + + if "${_show_date}"; then + cat <<-EOF + + $(__get_value_date_human ${2}) + + EOF + fi + + cat <<-EOF + + $(__get_value_title ${2}) + + EOF +} + +__generate_index() { + # 1: filename + # Generate and output to stdout the index of a file. + local _file + local _index_dir + + _index_dir="${src_dir}/$(__get_value "${1}" index)" + [ -f "${_index_dir}" ] && _index_dir=$(dirname "${_index_dir}") + [ ! -d "${_index_dir}" ] && return + + echo "" +} + +__generate_metadata() { + # 1: filename + # Generate metadata with title + local _title + local _show_title + + _show_title=$(__get_value "${1}" show_title || true) + + if [ "${_show_title}" = "" ]; then + _title=$(__get_value_title "${1}") + _title_id=$(echo ${_title} | __to_html_id) + + cat <<-EOF +

+ ${_title} +

+ EOF + fi +} + +__handle_md() { + # 1: filename + # Handle markdown files. + __lowdown "${1}" > "${tmp_file}.body" + + __generate_metadata "${1}" > "${tmp_file}.metadata" + + if [ ! "$(__get_value "${1}" index)" = "" ]; then + __generate_index "${1}" > "${tmp_file}.index" + else + [ -f "${tmp_file}.index" ] && rm "${tmp_file}.index" + fi + + __apply_template "$(__get_value "${1}" template)" > "${tmp_file}" + __install "${tmp_file}" "$(__get_out_filename "${1}")" +} + +__handle() { + # 1: filename + # Handle file depending on its filename and filetype. + case "${1}" in + *.md) + __handle_md "${1}" + ;; + *) + __install "${1}" "$(__get_out_filename ${1})" + ;; + esac +} diff --git a/rssg b/rssg new file mode 100755 index 0000000..16fdd2b --- /dev/null +++ b/rssg @@ -0,0 +1,48 @@ +#!/bin/sh + +. "$(dirname "${0}")/functions.sh" + +set -e + +current_date=$(date +"%a, %d %b %Y %H:%M:%S %z") + +cat <<-EOF > "${rss_out_file}" + + +${website_title} +${website_description} +${website_link} +$(date +"%Y") ${website_title} - All rights reserved +${website_email} +${website_email} +${current_date} +${current_date} +${website_generator} +${website_language} +https://validator.w3.org/feed/docs/rss2.html +1800 +EOF + +for file in $(__list_files_date "${rss_dir}"); do + __get_value "${file}" "draft" >/dev/null && continue + + title=$(__get_value_title "${file}") + date=$(__get_value_date_publication "${file}") + link="${website_link}/$(__get_final_filename "${file}")" + guid="$(echo "${link}" | "${__sha256}" | cut -d ' ' -f 1)" + description=$(__generate_rss_body "${file}") + cat <<-EOF >> "${rss_out_file}" + + ${title} + ${link} + ${date} + ${guid} + ${description} + + EOF +done + +cat <<-EOF >> "${rss_out_file}" + + +EOF diff --git a/ssg b/ssg new file mode 100755 index 0000000..9eb9c4b --- /dev/null +++ b/ssg @@ -0,0 +1,29 @@ +#!/bin/sh + + +. "$(dirname "${0}")/functions.sh" + +set -xe + +media_branch="media" +src_files="$(__list_files ${src_dir})" +dirs="${media_dir} \ + ${src_dir} \ + ${tmp_dir} \ + ${out_dir}" + +# Ensure directories exist. +for dir in ${dirs}; do + [ ! -d "${dir}" ] && mkdir "${dir}" +done + +# Generate output files from sources. +for src_file in ${src_files}; do + __handle "${src_file}" +done + +# Copy images +if git rev-parse media; then + git restore --source="${media_branch}" --worktree "img" + mv "img" "out" +fi -- cgit v1.2.3