Initial commit

This commit is contained in:
dogeystamp 2022-04-02 22:04:57 -04:00
commit f30048b9af
Signed by: dogeystamp
GPG Key ID: 7225FE3592EFFA38
17 changed files with 1005 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
site

8
src/_footer.html Normal file
View File

@ -0,0 +1,8 @@
</article>
<footer>
<hr>
<p>this site is generated with <a href="https://www.romanzolotarev.com/ssg.html">ssg</a></p>
<p>using font <a href="https://www.jetbrains.com/lp/mono/">JetBrains Mono</a> (<a href="https://github.com/JetBrains/JetBrainsMono/blob/master/OFL.txt">SIL Open Font License 1.1</a>)</p>
</footer>
</body>

33
src/_header.html Normal file
View File

@ -0,0 +1,33 @@
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="shortcut icon" type="image/svg" href="/WEBROOT/img/favicon.svg">
<title></title>
<meta name="description" content="dogeystamp's personal website">
<meta name="author" content="dogeystamp">
<style>
@font-face
{
font-family: JetBrainsMono;
src: url(/WEBROOT/fonts/JetBrainsMono-Regular.ttf);
}
@font-face
{
font-family: JetBrainsMono;
src: url(/WEBROOT/fonts/JetBrainsMono-Bold.ttf);
font-weight: bold;
}
</style>
<link rel="stylesheet" href="/WEBROOT/css/style.css">
</head>
<div class="header">
<img src="/WEBROOT/img/logo.svg" class="logo">
<b>dogeystamp</b>
<a href="/WEBROOT/index.html">home</a>
<a href="/WEBROOT/about.html">about me</a>
</div>
<article>

51
src/about.md Normal file
View File

@ -0,0 +1,51 @@
# hi! i'm dogeystamp.
## about me
I'm a Canadian high school student with an interest in tech and libre software.
## what i do
### competitive programming
I participate in some programming contests like the University of Waterloo's [CCC.](https://cemc.uwaterloo.ca/contests/computing.html)
In 2021's contest, I got a score of 48/75 in the senior division. [Here's](https://cemc.uwaterloo.ca/contests/computing.html) a repo with my solutions.
### random projects
There's a few miscellaneous projects I've done before, some of which have have articles in my blog.
### art
I also sometimes do digital art in Blender. Even though I'm not quite skilled with it, it's still quite fun.
![](img/golde3-thumb.jpg)
![](img/uwsnb-thumb.jpg)
As a side note, my logo is made in Inkscape, which I have even less experience in.
## contact information
* **matrix:** @dogey:d.nerdpol.ovh
* **discord:** dogeystamp#4056
* **email:** dogeystamp[at]disroot.org
#### pgp key:
```
----BEGIN PGP PUBLIC KEY BLOCK-----
mDMEAAAAABYJKwYBBAHaRw8BAQdAVq8Ixy8Upy2YrLEsVLXsbhK2qByl9tJJ07+g
mWqV/Wi0I2RvZ2V5c3RhbXAgPGRvZ2V5c3RhbXBAZGlzcm9vdC5vcmc+iGQEExYI
ABYFAgAAAAAJEHIl/jWS7/o4AhsDAh4BAACXAwD7BeO35GkDfR0/SQXZVE71aBaw
9zNVktGQTHOC+nYuFBYA/Rj2I5mkQg795p659fINKPA2u8NAW2WBWVdLJk+IHl8J
uDgEAAAAABIKKwYBBAGXVQEFAQEHQEgwybssSWkt0m7Ra+iOfipdOYldd2iX1dRj
3ATZum54AwEICYhhBBgWCAATBQIAAAAACRByJf41ku/6OAIbDAAA1bgBAPMTiHMK
d+71o6KSGYRLdb/16tzkdFc2ymHuWp6gmJmUAQDJhHUmj4wJLGI9N++79vXqGs1l
rixgx6WY32pgpumzCw==
=PGFl
-----END PGP PUBLIC KEY BLOCK-----
```
Note: This was generated using skeeto's [passphrase2pgp](https://github.com/skeeto/passphrase2pgp)
and it is normal that the date is set to the UNIX epoch.

101
src/css/style.css Normal file
View File

@ -0,0 +1,101 @@
body {
font-family: JetBrainsMono, monospace;
width: 85vmin;
margin: auto;
background: #101010;
color: #ffffff;
font-size: 125%;
}
a {
display: inline;
color: #aaaaaa;
}
p, pre {
margin-left: 2%;
}
h1, h2, h3, h4 {
margin-top: 3%;
color: #cccccc
}
h1, h2 {
margin-top: 5%;
font-size: 175%;
}
h3, h4 {
margin-left: 1%;
}
p {
margin-top: 3%;
}
img {
max-width: 70vmin;
height: auto;
}
pre, p > code {
font-size: 1.5vmin;
font-family: JetBrainsMono, monospace;
background: #090909;
padding: 0.5% 1% 0.5% 1%;
overflow: auto;
}
::-webkit-scrollbar {
width: 1vmin;
height: 1vmin;
}
::-webkit-scrollbar-track {
background-color: #050505;
}
::-webkit-scrollbar-thumb {
background-color: #151515;
}
.logo {
max-width: 5vh;
position: relative;
top: 1.5vh;
}
.header {
margin-top: 5%;
}
.header b {
font-size: 175%;
margin-right: 2%;
}
.header a {
margin-right: 1%;
}
footer {
margin-top: 15%;
font-size: 75%;
color: #cccccc;
}
.articles {
list-style-type: none;
margin-left: 2%;
padding: 0;
}
.articles li {
display: block;
margin-bottom: 2%;
}
.articles i {
display: block;
}

Binary file not shown.

Binary file not shown.

50
src/img/favicon.svg Normal file
View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="189.17741mm"
height="198.08646mm"
viewBox="0 0 189.17741 198.08646"
version="1.1"
id="svg991"
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
sodipodi:docname="logo.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview993"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.76078844"
inkscape:cx="236.59665"
inkscape:cy="417.98742"
inkscape:window-width="2460"
inkscape:window-height="1340"
inkscape:window-x="50"
inkscape:window-y="50"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs988" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-42.782484,47.224729)">
<path
id="path922"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3.51033;stroke-linejoin:round;stroke-dashoffset:73.3228;stop-color:#000000"
inkscape:transform-center-x="0.63497657"
inkscape:transform-center-y="-0.64853615"
d="m 137.40639,-47.224729 -12.76314,18.822753 -17.9685,-13.986433 -6.29832,21.830481 -21.4288,-7.772399 0.783116,22.7005102 -22.791543,-0.796333 7.788506,21.3478768 -21.923128,6.257019 14.030308,17.906666 -0.04265,0.02866 h 28.048289 c -0.07297,-1.238806 -0.223739,-2.679688 -0.446026,-4.457604 -0.551286,-4.409341 -1.026247,-8.890135 -1.054946,-9.956679 -0.02871,-1.066553 -0.322748,-6.197144 -0.654652,-11.401432 -0.618404,-9.6966148 0.342357,-13.69904583 4.175588,-17.3887428 3.66112,-3.524001 7.481597,-2.0816 17.945888,6.773918 6.40918,5.423851 10.76864,8.3657418 12.39522,8.3655538 7.81476,-9.9e-4 26.15732,-1.6149178 28.55648,-2.5128378 3.02912,-1.133666 8.99,-7.75317403 12.83919,-14.25819 1.87368,-3.166465 3.71431,-4.7759822 5.38572,-4.7539222 0.65405,0.0086 1.28188,0.267307 1.87609,0.7799532 0.76673,0.661476 1.58018,5.548096 1.88689,11.335411 0.63887,12.0564848 1.91761,14.1292208 11.24931,18.2367658 3.43355,1.511343 7.37373,3.856128 8.7561,5.209918 1.89218,1.853056 4.36385,7.416984 6.60715,14.027888 h 23.5798 l 14.02157,-17.868285 -21.9185,-6.272885 7.80341,-21.3422448 -22.79206,0.779952 0.79957,-22.6994842 -21.43498,7.756536 -6.28239,-21.834063 -17.97878,13.973637 z m -80.63676,111.79115 -13.987146,17.82479 21.919018,6.27288 -7.803923,21.342239 22.792574,-0.78046 -0.799562,22.69949 21.434449,-7.75654 6.28292,21.83458 17.97877,-13.97415 12.74927,18.83248 12.76365,-18.82327 17.9685,13.98643 6.29832,-21.82996 21.4288,7.77137 -0.78363,-22.69948 22.79155,0.79632 -7.78799,-21.348379 21.92312,-6.25702 -14.019,-17.89132 h -17.74549 c 0.0258,0.50616 0.0422,0.99503 0.038,1.44323 -0.0567,6.09202 -2.03525,15.17222 -4.38524,20.12574 -1.94708,4.10423 -8.10069,11.87094 -10.95541,13.82778 -5.55636,3.808739 -14.69088,7.956419 -20.02494,9.092279 -8.28314,1.76387 -27.08721,1.52179 -35.86608,-0.46163 C 104.75215,103.11994 96.885633,97.668001 89.606989,81.306741 87.51109,76.595491 85.166723,69.731601 83.833306,64.566421 Z"
sodipodi:nodetypes="ccccccccccccssscscsscscssccccccccccccccccccccccccccccccccssssscc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
src/img/golde3-thumb.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

BIN
src/img/golde3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 MiB

50
src/img/logo.svg Normal file
View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="189.17741mm"
height="198.08646mm"
viewBox="0 0 189.17741 198.08646"
version="1.1"
id="svg991"
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
sodipodi:docname="logo.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview993"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.76078844"
inkscape:cx="236.59665"
inkscape:cy="417.98742"
inkscape:window-width="2460"
inkscape:window-height="1340"
inkscape:window-x="50"
inkscape:window-y="50"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs988" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-42.782484,47.224729)">
<path
id="path922"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:3.51033;stroke-linejoin:round;stroke-dashoffset:73.3228;stop-color:#000000"
inkscape:transform-center-x="0.63497657"
inkscape:transform-center-y="-0.64853615"
d="m 137.40639,-47.224729 -12.76314,18.822753 -17.9685,-13.986433 -6.29832,21.830481 -21.4288,-7.772399 0.783116,22.7005102 -22.791543,-0.796333 7.788506,21.3478768 -21.923128,6.257019 14.030308,17.906666 -0.04265,0.02866 h 28.048289 c -0.07297,-1.238806 -0.223739,-2.679688 -0.446026,-4.457604 -0.551286,-4.409341 -1.026247,-8.890135 -1.054946,-9.956679 -0.02871,-1.066553 -0.322748,-6.197144 -0.654652,-11.401432 -0.618404,-9.6966148 0.342357,-13.69904583 4.175588,-17.3887428 3.66112,-3.524001 7.481597,-2.0816 17.945888,6.773918 6.40918,5.423851 10.76864,8.3657418 12.39522,8.3655538 7.81476,-9.9e-4 26.15732,-1.6149178 28.55648,-2.5128378 3.02912,-1.133666 8.99,-7.75317403 12.83919,-14.25819 1.87368,-3.166465 3.71431,-4.7759822 5.38572,-4.7539222 0.65405,0.0086 1.28188,0.267307 1.87609,0.7799532 0.76673,0.661476 1.58018,5.548096 1.88689,11.335411 0.63887,12.0564848 1.91761,14.1292208 11.24931,18.2367658 3.43355,1.511343 7.37373,3.856128 8.7561,5.209918 1.89218,1.853056 4.36385,7.416984 6.60715,14.027888 h 23.5798 l 14.02157,-17.868285 -21.9185,-6.272885 7.80341,-21.3422448 -22.79206,0.779952 0.79957,-22.6994842 -21.43498,7.756536 -6.28239,-21.834063 -17.97878,13.973637 z m -80.63676,111.79115 -13.987146,17.82479 21.919018,6.27288 -7.803923,21.342239 22.792574,-0.78046 -0.799562,22.69949 21.434449,-7.75654 6.28292,21.83458 17.97877,-13.97415 12.74927,18.83248 12.76365,-18.82327 17.9685,13.98643 6.29832,-21.82996 21.4288,7.77137 -0.78363,-22.69948 22.79155,0.79632 -7.78799,-21.348379 21.92312,-6.25702 -14.019,-17.89132 h -17.74549 c 0.0258,0.50616 0.0422,0.99503 0.038,1.44323 -0.0567,6.09202 -2.03525,15.17222 -4.38524,20.12574 -1.94708,4.10423 -8.10069,11.87094 -10.95541,13.82778 -5.55636,3.808739 -14.69088,7.956419 -20.02494,9.092279 -8.28314,1.76387 -27.08721,1.52179 -35.86608,-0.46163 C 104.75215,103.11994 96.885633,97.668001 89.606989,81.306741 87.51109,76.595491 85.166723,69.731601 83.833306,64.566421 Z"
sodipodi:nodetypes="ccccccccccccssscscsscscssccccccccccccccccccccccccccccccccssssscc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
src/img/uwsnb-thumb.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

BIN
src/img/uwsnb.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 MiB

20
src/index.md Normal file
View File

@ -0,0 +1,20 @@
# welcome to my website!
This is a place where I host random services and pages.
For more info about me, including contact info, check out [my about page.](about.html)
## services
### [gitea](../git/)
Local git server, along with frontend.
### [rockwell wiki](../rw/) (private)
MediaWiki for friends only.
### [bepp wiki](../wiki/) (private)
Another MediaWiki, also private.
## blog articles

101
src/posts/ssg.md Normal file
View File

@ -0,0 +1,101 @@
<!--- date: 2022-04-01 --->
<!--- title: website generation and deployment --->
# how this site works
Recently, I decided to renovate my website yet again.
For all the past iterations, it has been completely static.
However, this time, I have decided to use Roman Zolotarev's [ssg](https://www.romanzolotarev.com/ssg.html) static site generator.
## script patches
ssg is notably missing an article list, which I've implemented in [my own variation](../ssg6).
### metadata
In each article's md source file (including this one) I've added HTML comments with metadata like this.
```
<!--- date: 2022-04-01 --->
<!--- title: this site and ssg --->
```
### rendering the list
The main addition is a function render\_article\_list() that adds a bullet point list at the end of index.html.
For each page under posts/, it uses grep to find the date and title fields, sorts the pages, then creates a list of them in HTML format.
Although this implementation is hacky and probably easy to break, it does work.
```
render_article_list() {
urls="$1"
base_url="$2"
dst="$3"
src="$4"
sorted_urls=""
for i in $urls; do
if echo $i | grep '^posts/' > /dev/null; then
DATE=$(cat $dst/$i | grep '<!--- date: .*-.*-.* --->' | cut -d ' ' -f 3-3)
url="$date;$i"
sorted_urls="$(echo "$sorted_urls\n$i" | sort -r)"
fi
done
items=""
for i in $sorted_urls; do
CLEAN_DATE=$(cat $dst/$i | grep '<!--- date: .*-.*-.* --->' | cut -d ' ' -f 3-3)
TITLE=$(cat $dst/$i | grep '<!--- title: .*--->' | awk '{for(i=3;i<NF;i++) printf $i" " }' | xargs)
DATE=$(date -d "$CLEAN_DATE" +"%B %d, %Y")
item="<li><a href='$i'>$TITLE</a><i>$DATE</i></li>"
items="$items $item"
done
sed -i "s|</article>|<ul class='articles'>$items</ul></article>|g" "$dst/index.html"
}
```
Some extra code has been added to do this only on changes of posts/, so as to prevent repeatedly adding the list to the page.
### relative paths
Considering the header is cloned exactly the same onto each page in the original version of ssg, using relative paths
in pages inside a subdirectory is not possible.
To fix this, I've made it so that `/WEBROOT/` is a macro that is translated to a relative path to the web root within ssg.
For example,
```
<link rel="shortcut icon" type="image/svg" href="/WEBROOT/img/favicon.svg">
```
translates to
```
<link rel="shortcut icon" type="image/svg" href="../img/favicon.svg">
```
on this page.
The way this is implemented is that for each Markdown file rendered to HTML, ssg counts the slashes in the file path and adds the appropriate number of `../`.
```
root_prefix() {
stripped_path="$(echo $f | sed "s|\./||g")"
sub_count=$(echo "$stripped_path" | grep -o / | wc -l)
yes "../" | head -n $((sub_count)) | tr -d '\n'
}
```
# deployment
The entire site, including source code and the script, are part of a git repository. The webserver downloads it and deploys the site periodically using a cronjob.

295
src/ssg6 Normal file
View File

@ -0,0 +1,295 @@
#!/bin/sh -e
#
# https://rgz.ee/bin/ssg6
# Copyright 2018-2019 Roman Zolotarev <hi@romanzolotarev.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
main() {
test -n "$1" || usage
test -n "$2" || usage
test -n "$3" || usage
test -n "$4" || usage
test -d "$1" || no_dir "$1"
test -d "$2" || no_dir "$2"
src=$(readlink_f "$1")
dst=$(readlink_f "$2")
IGNORE=$(
if ! test -f "$src/.ssgignore"; then
printf ' ! -path "*/.*"'
return
fi
while read -r x; do
test -n "$x" || continue
printf ' ! -path "*/%s*"' "$x"
done <"$src/.ssgignore"
)
# files
title="$3"
h_file="$src/_header.html"
f_file="$src/_footer.html"
test -f "$f_file" && FOOTER=$(cat "$f_file") && export FOOTER
test -f "$h_file" && HEADER=$(cat "$h_file") && export HEADER
list_dirs "$src" |
(cd "$src" && cpio -pdu "$dst")
fs=$(
if test -f "$dst/.files"; then
list_affected_files "$src" "$dst/.files" | uniq
else
list_files "$1"
fi
)
if test -n "$fs"; then
echo "$fs" | tee "$dst/.files"
if echo "$fs" | grep -q '\.md$'; then
if test -x "$(which lowdown 2>/dev/null)"; then
echo "$fs" | grep '\.md$' |
render_md_files_lowdown "$src" "$dst" "$title"
else
if test -x "$(which Markdown.pl 2>/dev/null)"; then
echo "$fs" | grep '\.md$' |
render_md_files_Markdown_pl "$src" "$dst" "$title"
else
echo "couldn't find lowdown nor Markdown.pl"
exit 3
fi
fi
fi
echo "$fs" | grep '\.html$' |
render_html_files "$src" "$dst" "$title"
echo "$fs" | grep -Ev '\.md$|\.html$' |
(cd "$src" && cpio -pu "$dst")
fi
printf '[ssg] ' >&2
print_status 'file, ' 'files, ' "$fs" >&2
# sitemap
base_url="$4"
date=$(date +%Y-%m-%d)
urls=$(list_pages "$src")
test -n "$urls" &&
render_sitemap "$urls" "$base_url" "$date" >"$dst/sitemap.xml"
if echo "$fs" | grep "index.md$"; then
render_article_list "$urls" "$base_url" "$dst" "$src"
fi
print_status 'url' 'urls' "$urls" >&2
echo >&2
}
readlink_f() {
file="$1"
cd "$(dirname "$file")"
file=$(basename "$file")
while test -L "$file"; do
file=$(readlink "$file")
cd "$(dirname "$file")"
file=$(basename "$file")
done
dir=$(pwd -P)
echo "$dir/$file"
}
print_status() {
test -z "$3" && printf 'no %s' "$2" && return
echo "$3" | awk -v singular="$1" -v plural="$2" '
END {
if (NR==1) printf NR " " singular
if (NR>1) printf NR " " plural
}'
}
usage() {
echo "usage: ${0##*/} src dst title base_url" >&2
exit 1
}
no_dir() {
echo "${0##*/}: $1: No such directory" >&2
exit 2
}
list_dirs() {
cd "$1" && eval "find . -type d ! -name '.' ! -path '*/_*' $IGNORE"
}
list_files() {
cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE"
}
list_dependant_files() {
e="\\( -name '*.html' -o -name '*.md' -o -name '*.css' -o -name '*.js' \\)"
cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE $e"
}
list_newer_files() {
cd "$1" && eval "find . -type f ! -name '.' $IGNORE -newer $2"
}
has_partials() {
grep -qE '^./_.*\.html$|^./_.*\.js$|^./_.*\.css$'
}
list_affected_files() {
fs=$(list_newer_files "$1" "$2")
if echo "$fs" | has_partials; then
list_dependant_files "$1"
else
echo "$fs"
fi
if echo "$fs" | grep "posts/"; then
if ! echo "$fs" | grep "index\.md"; then
echo index.md
fi
fi
}
root_prefix() {
stripped_path="$(echo $f | sed "s|\./||g")"
sub_count=$(echo "$stripped_path" | grep -o / | wc -l)
yes "../" | head -n $((sub_count)) | tr -d '\n'
}
render_html_files() {
while read -r f; do
render_html_file "$3" <"$1/$f" >"$2/$f"
done
}
render_md_files_lowdown() {
while read -r f; do
lowdown \
--html-no-escapehtml \
--html-no-skiphtml \
--parse-no-metadata \
--parse-no-autolink <"$1/$f" |
render_html_file "$3" \
>"$2/${f%\.md}.html"
done
}
render_md_files_Markdown_pl() {
while read -r f; do
Markdown.pl <"$1/$f" |
render_html_file "$3" \
>"$2/${f%\.md}.html"
done
}
render_html_file() {
# h/t Devin Teske
awk -v title="$1" -v pfx="$(root_prefix)" '
{ body = body "\n" $0 }
END {
body = substr(body, 2)
if (body ~ /<\/?[Hh][Tt][Mm][Ll]/) {
print body
exit
}
if (match(body, /<[[:space:]]*[Hh]1(>|[[:space:]][^>]*>)/)) {
t = substr(body, RSTART + RLENGTH)
sub("<[[:space:]]*/[[:space:]]*[Hh]1.*", "", t)
gsub(/^[[:space:]]*|[[:space:]]$/, "", t)
if (t) title = t " &mdash; " title
}
n = split(ENVIRON["HEADER"], header, /\n/)
for (i = 1; i <= n; i++) {
if (match(tolower(header[i]), "<title></title>")) {
head = substr(header[i], 1, RSTART - 1)
tail = substr(header[i], RSTART + RLENGTH)
print head "<title>" title "</title>" tail
} else if (match(header[i], "/WEBROOT/")){
head = substr(header[i], 1, RSTART - 1)
tail = substr(header[i], RSTART + RLENGTH)
print head pfx tail
} else print header[i]
}
print body
print ENVIRON["FOOTER"]
}'
}
list_pages() {
e="\\( -name '*.html' -o -name '*.md' \\)"
cd "$1" && eval "find . -type f ! -path '*/.*' ! -path '*/_*' $IGNORE $e" |
sed 's#^./##;s#.md$#.html#;s#/index.html$#/#'
}
render_article_list() {
urls="$1"
base_url="$2"
dst="$3"
src="$4"
sorted_urls=""
for i in $urls; do
if echo $i | grep '^posts/' > /dev/null; then
DATE=$(cat $dst/$i | grep '<!--- date: .*-.*-.* --->' | cut -d ' ' -f 3-3)
url="$date;$i"
sorted_urls="$(echo "$sorted_urls\n$i" | sort -r)"
fi
done
items=""
for i in $sorted_urls; do
CLEAN_DATE=$(cat $dst/$i | grep '<!--- date: .*-.*-.* --->' | cut -d ' ' -f 3-3)
TITLE=$(cat $dst/$i | grep '<!--- title: .*--->' | awk '{for(i=3;i<NF;i++) printf $i" " }' | xargs)
DATE=$(date -d "$CLEAN_DATE" +"%B %d, %Y")
item="<li><a href='$i'>$TITLE</a><i>$DATE</i></li>"
items="$items $item"
done
sed -i "s|</article>|<ul class='articles'>$items</ul></article>|g" "$dst/index.html"
}
render_sitemap() {
urls="$1"
base_url="$2"
date="$3"
echo '<?xml version="1.0" encoding="UTF-8"?>'
echo '<urlset'
echo 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
echo 'xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9'
echo 'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"'
echo 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
echo "$urls" |
sed -E 's#^(.*)$#<url><loc>'"$base_url"'/\1</loc><lastmod>'"$date"'</lastmod><priority>1.0</priority></url>#'
echo '</urlset>'
}
main "$@"

295
ssg6 Executable file
View File

@ -0,0 +1,295 @@
#!/bin/sh -e
#
# https://rgz.ee/bin/ssg6
# Copyright 2018-2019 Roman Zolotarev <hi@romanzolotarev.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
main() {
test -n "$1" || usage
test -n "$2" || usage
test -n "$3" || usage
test -n "$4" || usage
test -d "$1" || no_dir "$1"
test -d "$2" || no_dir "$2"
src=$(readlink_f "$1")
dst=$(readlink_f "$2")
IGNORE=$(
if ! test -f "$src/.ssgignore"; then
printf ' ! -path "*/.*"'
return
fi
while read -r x; do
test -n "$x" || continue
printf ' ! -path "*/%s*"' "$x"
done <"$src/.ssgignore"
)
# files
title="$3"
h_file="$src/_header.html"
f_file="$src/_footer.html"
test -f "$f_file" && FOOTER=$(cat "$f_file") && export FOOTER
test -f "$h_file" && HEADER=$(cat "$h_file") && export HEADER
list_dirs "$src" |
(cd "$src" && cpio -pdu "$dst")
fs=$(
if test -f "$dst/.files"; then
list_affected_files "$src" "$dst/.files" | uniq
else
list_files "$1"
fi
)
if test -n "$fs"; then
echo "$fs" | tee "$dst/.files"
if echo "$fs" | grep -q '\.md$'; then
if test -x "$(which lowdown 2>/dev/null)"; then
echo "$fs" | grep '\.md$' |
render_md_files_lowdown "$src" "$dst" "$title"
else
if test -x "$(which Markdown.pl 2>/dev/null)"; then
echo "$fs" | grep '\.md$' |
render_md_files_Markdown_pl "$src" "$dst" "$title"
else
echo "couldn't find lowdown nor Markdown.pl"
exit 3
fi
fi
fi
echo "$fs" | grep '\.html$' |
render_html_files "$src" "$dst" "$title"
echo "$fs" | grep -Ev '\.md$|\.html$' |
(cd "$src" && cpio -pu "$dst")
fi
printf '[ssg] ' >&2
print_status 'file, ' 'files, ' "$fs" >&2
# sitemap
base_url="$4"
date=$(date +%Y-%m-%d)
urls=$(list_pages "$src")
test -n "$urls" &&
render_sitemap "$urls" "$base_url" "$date" >"$dst/sitemap.xml"
if echo "$fs" | grep "index.md$"; then
render_article_list "$urls" "$base_url" "$dst" "$src"
fi
print_status 'url' 'urls' "$urls" >&2
echo >&2
}
readlink_f() {
file="$1"
cd "$(dirname "$file")"
file=$(basename "$file")
while test -L "$file"; do
file=$(readlink "$file")
cd "$(dirname "$file")"
file=$(basename "$file")
done
dir=$(pwd -P)
echo "$dir/$file"
}
print_status() {
test -z "$3" && printf 'no %s' "$2" && return
echo "$3" | awk -v singular="$1" -v plural="$2" '
END {
if (NR==1) printf NR " " singular
if (NR>1) printf NR " " plural
}'
}
usage() {
echo "usage: ${0##*/} src dst title base_url" >&2
exit 1
}
no_dir() {
echo "${0##*/}: $1: No such directory" >&2
exit 2
}
list_dirs() {
cd "$1" && eval "find . -type d ! -name '.' ! -path '*/_*' $IGNORE"
}
list_files() {
cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE"
}
list_dependant_files() {
e="\\( -name '*.html' -o -name '*.md' -o -name '*.css' -o -name '*.js' \\)"
cd "$1" && eval "find . -type f ! -name '.' ! -path '*/_*' $IGNORE $e"
}
list_newer_files() {
cd "$1" && eval "find . -type f ! -name '.' $IGNORE -newer $2"
}
has_partials() {
grep -qE '^./_.*\.html$|^./_.*\.js$|^./_.*\.css$'
}
list_affected_files() {
fs=$(list_newer_files "$1" "$2")
if echo "$fs" | has_partials; then
list_dependant_files "$1"
else
echo "$fs"
fi
if echo "$fs" | grep "posts/"; then
if ! echo "$fs" | grep "index\.md"; then
echo index.md
fi
fi
}
root_prefix() {
stripped_path="$(echo $f | sed "s|\./||g")"
sub_count=$(echo "$stripped_path" | grep -o / | wc -l)
yes "../" | head -n $((sub_count)) | tr -d '\n'
}
render_html_files() {
while read -r f; do
render_html_file "$3" <"$1/$f" >"$2/$f"
done
}
render_md_files_lowdown() {
while read -r f; do
lowdown \
--html-no-escapehtml \
--html-no-skiphtml \
--parse-no-metadata \
--parse-no-autolink <"$1/$f" |
render_html_file "$3" \
>"$2/${f%\.md}.html"
done
}
render_md_files_Markdown_pl() {
while read -r f; do
Markdown.pl <"$1/$f" |
render_html_file "$3" \
>"$2/${f%\.md}.html"
done
}
render_html_file() {
# h/t Devin Teske
awk -v title="$1" -v pfx="$(root_prefix)" '
{ body = body "\n" $0 }
END {
body = substr(body, 2)
if (body ~ /<\/?[Hh][Tt][Mm][Ll]/) {
print body
exit
}
if (match(body, /<[[:space:]]*[Hh]1(>|[[:space:]][^>]*>)/)) {
t = substr(body, RSTART + RLENGTH)
sub("<[[:space:]]*/[[:space:]]*[Hh]1.*", "", t)
gsub(/^[[:space:]]*|[[:space:]]$/, "", t)
if (t) title = t " &mdash; " title
}
n = split(ENVIRON["HEADER"], header, /\n/)
for (i = 1; i <= n; i++) {
if (match(tolower(header[i]), "<title></title>")) {
head = substr(header[i], 1, RSTART - 1)
tail = substr(header[i], RSTART + RLENGTH)
print head "<title>" title "</title>" tail
} else if (match(header[i], "/WEBROOT/")){
head = substr(header[i], 1, RSTART - 1)
tail = substr(header[i], RSTART + RLENGTH)
print head pfx tail
} else print header[i]
}
print body
print ENVIRON["FOOTER"]
}'
}
list_pages() {
e="\\( -name '*.html' -o -name '*.md' \\)"
cd "$1" && eval "find . -type f ! -path '*/.*' ! -path '*/_*' $IGNORE $e" |
sed 's#^./##;s#.md$#.html#;s#/index.html$#/#'
}
render_article_list() {
urls="$1"
base_url="$2"
dst="$3"
src="$4"
sorted_urls=""
for i in $urls; do
if echo $i | grep '^posts/' > /dev/null; then
DATE=$(cat $dst/$i | grep '<!--- date: .*-.*-.* --->' | cut -d ' ' -f 3-3)
url="$date;$i"
sorted_urls="$(echo "$sorted_urls\n$i" | sort -r)"
fi
done
items=""
for i in $sorted_urls; do
CLEAN_DATE=$(cat $dst/$i | grep '<!--- date: .*-.*-.* --->' | cut -d ' ' -f 3-3)
TITLE=$(cat $dst/$i | grep '<!--- title: .*--->' | awk '{for(i=3;i<NF;i++) printf $i" " }' | xargs)
DATE=$(date -d "$CLEAN_DATE" +"%B %d, %Y")
item="<li><a href='$i'>$TITLE</a><i>$DATE</i></li>"
items="$items $item"
done
sed -i "s|</article>|<ul class='articles'>$items</ul></article>|g" "$dst/index.html"
}
render_sitemap() {
urls="$1"
base_url="$2"
date="$3"
echo '<?xml version="1.0" encoding="UTF-8"?>'
echo '<urlset'
echo 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
echo 'xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9'
echo 'http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"'
echo 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
echo "$urls" |
sed -E 's#^(.*)$#<url><loc>'"$base_url"'/\1</loc><lastmod>'"$date"'</lastmod><priority>1.0</priority></url>#'
echo '</urlset>'
}
main "$@"