project management
The first assignment was setting up a personal site for documenting this and all future assignments, as well as sketching out the tentative final project - literally and figuratively. This post will be split in two parts accordingly.
website setup
A part of this assignment was also to get familiar with git. Since I have worked extensively with git in the past, this was no problem. However I had not used GitLab before, so I spent some time getting familiar with the hierarchical organisation principles and the CI/CD subsystems GitLab provides.
cms choice
For managing the site, I chose to deploy a version of a content management system I built
as part of my bachelor thesis - mmmfs
. mmmfs
is designed as a highly customizable multimedia
wiki-like system for personal documentation and computing. In its full implementation, it can embed any
kind of content (images, videos, graphs, interactive widgets) from anywhere inside the system as well as from
external sources in very flexible ways. However to keep the deployment and repo size small and comply with the
statically-served content policy, I chose to deploy it differently from the main installation on my personal
website. Rather than converting and serving content on demand, in this setup the system is run as part of the
GitLab CI service whenever the content is pushed to the repository, and compiles every ‘fileder’ in the system
into a static HTML file.
This mode of static content generation used to be a feature of mmmfs
, but was sacrificed for the sake of
in-system editing as part of my bachelor thesis. I decided to restore it for the FabAcademy, because I wanted
to use the same system here that I use for the main page. I prefer to use mmmfs
here because it will allow me
to share content between this repository and my main website in the future, and it means that I can keep my
visual identity consistent between the two contexts. I will also be able to keep customizing it and add
capabilities like inline 3d model previews etc as I go along documenting.
technical setup
For continuous delivery for my own website, I had already dockerized the dependencies for running the ‘live’
server of my blog with the following Dockerfile
:
FROM nickblah/lua:5.3-luarocks-stretch
RUN echo "deb http://ppa.launchpad.net/jonathonf/tup/ubuntu xenial main" \
>/etc/apt/sources.list.d/tup.list
RUN apt-get update && \
apt-get install -y --allow-unauthenticated \
build-essential m4 tup sassc \
libmarkdown2-dev libsqlite3-dev libssl-dev
RUN luarocks install discount DISCOUNT_INCDIR=/usr/include/x86_64-linux-gnu
RUN luarocks install sqlite3 && \
luarocks install moonscript && \
luarocks install http && \
luarocks install lua-cjson 2.1.0-1
COPY . /code
WORKDIR /code
RUN tup init && tup generate --config tup.docker.config build-static.sh && ./build-static.sh
EXPOSE 8000
ENTRYPOINT ["moon", "build/server.moon", "fs", "0.0.0.0", "8000"]
While the EXPOSE
and ENTRYPOINT
directives here are superfluous for the static site workflow,
I chose to re-use the image as-is so that I would only have to maintain one docker image for this project,
rather than dealing with two slightly different variants. Thankfully the GitLab CI configuration turned out
to be very flexible in this regard.
The docker image usually is rebuilt on my own server whenever I push to the git repository and deployed there
(you can read the blog post documenting my simple homebuilt git-ci setup here), but not published publicly
online. For GitLab to find my docker image, I had to manually publish it on dockerhub as s0lll0s/mmm
.
Once the image was available, I could move on to configuring GitLab’s CI system using the .gitlab-ci.yml
file.
Using the documentation I figured out that by overriding the entrypoint with an empty command I could get the
shell-command injection working as it was intended. Compared to the default .gitlab-ci.yml
using marked
,
I could drop the before_script
part, since all my dependencies come pre-installed in the docker image.
In the pages
part on the other hand there are a few more things to do:
image:
name: s0lll0s/mmm
entrypoint: [""]
pages:
script:
- cd /code
- "patch -p0 <$CI_PROJECT_DIR/static/layout/'text$patch.patch'"
- moon build/render_all.moon fs:$CI_PROJECT_DIR public /2020/labs/opendot/students/sol-bekic
- cp -r public $CI_PROJECT_DIR/public
artifacts:
paths:
- public
only:
- master
First the shell has to change into the /code
directory, where my CMS code is located (compare the Dockerfile
above).
In theory I could set the LUA_PATH
and LUA_CPATH
appropriately and run the code directly from inside the site
repository, but it was a bit easier to set it up this way. After this, I apply a patch file to change some
details about the page layout that are currently hardcoded for my main website. This is what the patch file looks like
currently:
--- mmm/mmmfs/layout.moon 2020-01-30 12:03:40.601460375 +0100
+++ mmm/mmmfs/layout.fab.moon 2020-01-30 12:58:20.264865069 +0100
@@ -36,7 +36,7 @@
h1 {
navigate_to '', logo
span {
- span 'mmm', class: 'bold'
+ span 'fab', class: 'bold'
'​'
'.s‑ol.nu'
}
@@ -51,9 +51,7 @@
}
aside {
- navigate_to '/about', 'about me'
- navigate_to '/portfolio', 'portfolio'
- navigate_to '/games', 'games'
- navigate_to '/projects', 'other'
+ navigate_to '/', 'about me'
+ navigate_to '/log', 'log'
a {
href: 'mailto:s%20[removethis]%20[at]%20s-ol.nu'
'contact'
@@ -78,8 +76,6 @@
iconlink 'https://github.com/s-ol', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/github.svg', 'github'
iconlink 'https://merveilles.town/@s_ol', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/mastodon.svg', 'mastodon'
iconlink 'https://twitter.com/S0lll0s', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/twitter.svg', 'twitter'
- iconlink 'https://webring.xxiivv.com/#random', 'https://webring.xxiivv.com/icon.black.svg', 'webring',
- { height: '1.3em', 'margin-left': '.3em', 'margin-top': '-0.12em' }
}
}
@@ -104,8 +100,8 @@
<meta property=\"og:title\" content=#{e title} />
<meta property=\"og:type\" content=\"website\" />
- <meta property=\"og:url\" content=\"https://mmm.s-ol.nu#{@path}/\" />
- <meta property=\"og:site_name\" content=\"mmm\" />"
+ <meta property=\"og:url\" content=\"https://fab.s-ol.nu#{@path}/\" />
+ <meta property=\"og:site_name\" content=\"sol bekic - fab academy 2020\" />"
if desc = @get 'description: text/plain'
meta ..= "
This again is a quick solution to allow me to change things about the CMS between my main website and this project log respectively, without fully diverting the two instances into different projects. In the future I would like to integrate the website layout into the ‘content’ of the CMS itself, so that the system and its looks are completely separate and nothing is hardcoded, but you can read more about that in my thesis linked above.
One thing that took me a while to realize was that my site was being served as a sub-site deep in fabacademy.org
, so
that all of my page URLs start with /2020/labs/opendot/students/sol-bekic
, whereas on my own site everything is at
the root (/
). To cope with this I had to revisit a couple of places in mmmfs
‘ code, since absolute links are used
almost everywhere: for links between pages, inclusion of assets like images and videos, and for the sitewide
stylesheet. My solution was to add a configuration flag for the static site export that simply prepends
the prefix string to all URLs produced in the system. You can see me passing the prefix as an option to the build script
in the .gitlab-ci.yml
above.
repository layout
I added three top-level Fileders:
static
, a hidden fileder that contains the stylesheet, some web runtime components (like the syntax highlighting plugin) and the patch file mentioned above,about
, containing the short about section,log
, containg the weekly logs (such as this one you are currently reading).
The about page and this log post are composed in Markdown, but with mmmfs
nearly any format can theoretically be
supported, and all supported formats and filetypes can be mixed and matched freely across and inside pages.
It is also possible to describe pages using code that is executed on demand and can compose pages based on other data
stored in the system. This is how the main page is realized: because the about section is so short, I wanted to show it
on the main page rather than on its own, and to also preview the weekly log index alongside it.
The main page Facet has the type text/moonscript -> fn -> mmm/dom
(read as: A MoonScript file that,
when parsed, yields a function that, when executed, returns a HTML DOM snippet), and looks like this:
import div from require 'mmm.dom'
-- delegate to the two child fileders
=>
div for child in *@children
continue if child\get 'hidden: bool'
div (child\get 'mmm/dom'), class: 'well'
As for all pages, when mmmfs
renders this page, it attempts a conversion to text/html
. A conversion path is searched
for using (all combinations of) the supported individual conversions. Here is an excerpt of the graph of possible
conversions mmmfs
currently enumerates for this specific facet:
The cheapest path (by cumulative cost
) that is found is the intended one: first the MoonScript is parsed, then the
resulting function is executed, and finally the resulting DOM snippet is embedded in the HTML layout template.
When the function (the =>
and everything indented past it in the snippet above) is executed, it loops over all
children of its Fileder (the main directory of this repository). It skips all Fileders that have a hidden
Facet
that evaluates to false
when converted to a boolean value (that is currently only the static
fileder) and embeds
all other fileders in <div>
s with the class well
. The embedded divs are all grouped together in another <div>
due
to a technical limitation that only allows passing single elements as mmm/dom
objects currently.
image optimization
For image optimization, I tried to use MozJPEG first, but found that the workflow for lossy conversion was a bit less straightforward than I assumed. I then found jpegoptim to be easier to use.
I will add all images as high-quality files locally with a hq:
facet name, which I have excluded in the repository
.gitignore
file, so they will not be committed. I can then duplicate these files and compress them before pushing
the compressed version like so:
$ cp "hq: image$jpeg.jpg" "image$jpeg.jpg"
$ jpegoptim -sS100 "image$jpeg.jpg"
The option -s
instructs jpegoptim
to strip all metadata, and -S 100
tells it to bring the file down as close as
possible to a 100 kilobyte size as possible. For the image below this has worked out pretty well, but I will keep
checking the image quality and adjusting settings as necessary as I keep adding pictures in the upcoming weeks.
final project sketch
For the final project I think I want to create a custom ergonomic keyboard. I am currently typing this from an Ergodox EZ, which, as an ortholinear and split keyboard, has some ergonomic benefits, but i am still finding that the individual key positions don’t match my hand shape as well as I would like to.
I have heard some good things about curved designs like the Dactyl and the Kinesis Advantage, but I am more interested in making a custom design based on some practical testing and modeling with my own hand shape.
I would also like to integrate a single-board computer and a battery pack into the two halfes of the keyboard respectively, so that the keyboard can be used as a standalone computer for very simple jobs when it is not plugged into another computer using USB. For this purpose it would be great to have a couple of features that keyboards don’t usually have:
- host / sub switch to select between the two modes of operation
- LED battery indicators
- HDMI output
- small OLED screen with serial login
- USB host ports for connecting externals to the SBC. Ideally these could be forwarded to the host PC when the keyboard is plugged in over USB (like a hub), but I will have to investigate if that is technically feasible.
It would also be nice to take advantage of the fact that the keyboard is effectively a fully-fledged computer with storage and even network connectivity with software features such as using the keyboard as a hardware password manager (this idea was shamelessly stolen from the excellent MNT Reform 2 FOSS Laptop Project).