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 |