summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain Gonçalves <me@rgoncalves.se>2024-05-15 12:51:49 +0200
committerRomain Gonçalves <me@rgoncalves.se>2024-05-15 12:51:49 +0200
commitc5ad7625aaaf32ba407729aa63b3d48edb5eb910 (patch)
treeb0e05ab5ea0180e102c094383af53c7b787f6c57
downloadssg-c5ad7625aaaf32ba407729aa63b3d48edb5eb910.tar.gz
feat: init static site generator
-rw-r--r--functions.sh257
-rwxr-xr-xrssg48
-rwxr-xr-xssg29
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/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g; s/'"'"'/\&#39;/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
+}
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}"
+<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
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
remember that computers suck.