diff options
author | Romain Gonçalves <me@rgoncalves.se> | 2024-05-15 12:51:49 +0200 |
---|---|---|
committer | Romain Gonçalves <me@rgoncalves.se> | 2024-05-15 12:51:49 +0200 |
commit | c5ad7625aaaf32ba407729aa63b3d48edb5eb910 (patch) | |
tree | b0e05ab5ea0180e102c094383af53c7b787f6c57 | |
download | ssg-c5ad7625aaaf32ba407729aa63b3d48edb5eb910.tar.gz |
feat: init static site generator
-rw-r--r-- | functions.sh | 257 | ||||
-rwxr-xr-x | rssg | 48 | ||||
-rwxr-xr-x | ssg | 29 |
3 files changed, 334 insertions, 0 deletions
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; 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 + <span> + $(__get_value_date_human ${2}) + </span> + EOF + fi + + cat <<-EOF + <a href="/$(__get_final_filename ${2})"> + $(__get_value_title ${2}) + </a> + 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 "<ul class=\"index\">" + + for _file in $(__list_files_date "${_index_dir}"); do + [ "${1}" = "${_file}" ] && continue + __get_value "${_file}" "draft" >/dev/null && continue + + echo "<li>" + __generate_index_line "${1}" "${_file}" + echo "</li>" + done + + echo "</ul>" +} + +__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 + <h1 id="${_title_id}"> + ${_title} + </h1> + 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 +} @@ -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}" +<rss version="2.0"> +<channel> +<title>${website_title}</title> +<description>${website_description}</description> +<link>${website_link}</link> +<copyright>$(date +"%Y") ${website_title} - All rights reserved</copyright> +<managingEditor>${website_email}</managingEditor> +<webMaster>${website_email}</webMaster> +<lastBuildDate>${current_date}</lastBuildDate> +<pubDate>${current_date}</pubDate> +<generator>${website_generator}</generator> +<language>${website_language}</language> +<docs>https://validator.w3.org/feed/docs/rss2.html</docs> +<ttl>1800</ttl> +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}" + <item> + <title>${title}</title> + <link>${link}</link> + <pubDate>${date}</pubDate> + <guid>${guid}</guid> + <description>${description}</description> + </item> + EOF +done + +cat <<-EOF >> "${rss_out_file}" +</channel> +</rss> +EOF @@ -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 |