.drone | ||
clay | ||
drafts | ||
files | ||
fonts | ||
images | ||
images-src | ||
infra | ||
js | ||
lib | ||
pages | ||
posts | ||
src | ||
templates | ||
.drone.jsonnet | ||
.drone.yml | ||
.envrc | ||
.ghci | ||
.gitignore | ||
blog-rekahsoft-ca.cabal | ||
bootstrap.sh | ||
channels.scm | ||
guix.scm | ||
LICENSE | ||
README.org | ||
robots.txt | ||
Setup.hs |
Source Code for #! Lambda Slang
- Features
- Tools
- License
- Repository Structure
- Guix Development Environment
- Building a Release
- Deploying the Site
- Writing a Blog Post
- DOING Vendor external libraries using Guix
- Makefile
- Continuous Integration & Delivery
- Known Issues
#! Lambda Slang is the personal technical blog of Collin Doering, built using software that respects our freedoms.
Features
- Single Page Application (SPA)
- Write blog posts and pages in markdown
- Support for math markup via MathJax
- RSS/Atom feed
Tools
The creation of this website was made possible by the following open source tools and libraries:
- Hakyll is used to generate site from static files
- Clay is used for CSS pre-processing
- Skeleton is used for CSS boilerplate
- MathJax is used for rendering mathematics
- JQuery is used for various DOM manipulations
- Gnu Guix is used to manage development environments and packaging
- Inkscape and the Gimp were used to create various images/artwork
- Gnu Free Fonts, specifically FreeMono is used as main font
- Gnu Emacs because there is no place like home; and no greater editor!
License
Simply put, you're welcome to use the code used to generate this site though there are a few restrictions:
- Any images and artwork that embody the likeness of "#! Lambda Slang" are not to be distributed or used and are strictly copyright
- The content of pages and posts can be used with attribution, providing you aren't making money off of it
Various licenses (GPLv3, Creative Commons BY-NC-SA License, and Creative Commons BY-NC-ND License) are deployed dependent on which part of the site is in question. Please see the LICENSE file for full details.
Repository Structure
-
blog-rekahsoft-ca.cabal
- Cabal package definition.
-
bootstrap.sh
- Generate development scripts, Makefile, and setup vendor links based on this literate configurationl
-
channels.scm
- Guix Channel File.
-
clay/*.hs
- Clay source files.
-
css/*.css
- CSS source files (will be minified).
-
drafts/*.md
- Draft posts.
-
files/images/
- Folder for images used in posts.
-
files/source/
- Folder for source code used in blog posts/pages.
-
fonts/*.{ttf,woff,...}
- Font files
-
guix.scm
- Guix package definition for this site-builder and its resulting site.
-
images/*
- Folder for images used on pages or in templates.
-
images-src/*
- Folder for the image source files (should be 1-to-1 with files in
images/*
). -
infra/
- Infrastructure folder; contains terraform based Infrastructure As Code (IAC).
-
infra/channels.scm
- Symlink to
../channels.scm
(can be independent if needed). -
infra/*.tf
- Terraform source files.
-
infra/*.tfvars
- Terraform variables files for each environment (non-secrets).
-
infra/Makefile
- Makefile used for terraform deployments.
-
infra/manifest.scm
- Guix manifest that defines the necessary deployment environment.
-
js/*.js
- Javascript files.
-
lib/*
- Javascript libraries.
-
LICENSE
- License file.
-
pages/*.markdown
- Page content.
-
posts/*.markdown
- Blog posts.
-
README.org
- Org-mode documentation.
-
robots.txt
- Robot Exclusion Protocol file.
-
Setup.hs
- Cabal build script for this site.
-
src/*.hs
- Hakyll powered site builder.
-
templates/
- Folder for all template files.
-
templates/default.html
- Entry point template (defines html document used for all pages).
-
templates/pages/*.html
- Html page templates (correspond 1-to-1 with pages/*.markdown files).
-
templates/partials/*.html
- Partial template files, for use within templates.
-
templates/tag-page.html
- Template for creating pages about tags with a specific tag.
Guix Development Environment
Gnu Guix is used to package this project and manage its dependencies, as well as to provide reproducible development environments.
Prerequisites
Quick Start
First run the bootstrap script, which uses this documentation to generate a Makefile
that
can be used for development.
./bootstrap.sh
Then run the development 'auto-watching' environment:
make
This starts a containerized local development environment that uses ghcid to watch haskell sources and restart hakyll's site watch feature when changes occur. The site will be available at http://localhost:3000, and will automatically rebuild as site files change (templates, post, pages, etc..).
Start Development Environment
The development environment is defined by the following files:
- channels.scm
- Specifically defines a set of available software, their versions and their build recipe.
- guix.scm
- Defines the package for this site,
blog-rekahsoft-ca
.
To start a development environment, run the following:
guix time-machine -C channels.scm -- shell -CN -E '^LANG$' -E '^TERM$' -f guix.scm -Df guix.scm $@
This uses the guix time-machine feature to ensure the development environment is reproducible
by supplying a set of guix channels, effectively pinning all software versions used. The guix
shell command is used within the time-machine to start a development environment in a
container (-C
), which shares the hosts network namespace (-N
). The environment variable
LANG
is passed into the container to ensure locales work as expected; without this, site
building will fail! Additionally, the environment variable TERM
is passed into the
container to ensure the development shell behaves correctly. The option -f guix.scm
loads
the blog-rekahsoft-ca
package, and -Df guix.scm
indicates that development dependencies
of the blog-rekahsoft-ca
package should be included in the environment.
Deployment Environment
Gnu Guix is used, similar to in the previous section, to create environments with all tools
necessary for deployments, with a notable difference being a guix.scm
file is not provided
or needed, as the deployment environment is used solely for its side effects.
- infra/channels.scm
- Symlink to ../channels.scm to make the guix cli workflow nicer when
in the
infra
directory. Technically this doesn't need to be a symlink, and could be a different set of channels or version of channels compared to the channels file at the top-level of the repository, however this would complicate Composing Site Development and Deployment Environments, so its preferred that all guix environments for the project, including the development and deployment environment use the same set of Guix channels. - infra/manifest.scm
- Defines packages required for deployment of this site.
To start a deployment environment, run the following:
cd infra
guix time-machine -C channels.scm -- shell -CN -E '^LANG$' -E '^TERM$' -E '^AWS.*$'
Composing Site Development and Deployment Environments
guix time-machine -C channels.scm -- shell -CN -E '^LANG$' -E '^TERM$' -E '^AWS.*$' -f guix.scm -Df guix.scm -m infra/manifest.scm $@
Hakyll Site Commands
Build Site
This website is built from a collection of markdown files and templates that are processed by
pandoc and are stitched together using Hakyll. To build the html/css/jss and all other assets
required to deploy and distribute the site, the hakyll derived site-builder,
blog-rekahsoft-ca
must be invoked. For convenience, an alias site
is provided for the
site builder as part of its guix package. Here is it being used to build the site:
site build
Clean Site
Building the site has the side effect of writing a couple files/directories to disk as a
result of the build process. In some cases, its useful to start of with a clean slate and
remove any files that were generated for the site. To so so, the clean
sub-command can be
used:
site clean
Watch
During development of new content or adjustments to the site, it is useful to autocompile upon changes to any site files (templates, pages, posts, etc..). This functionality is provided by Hakyll.
site watch
TODO
site deploy
command
site deploy
Clean up Guix Store
guix gc --list-dead | grep -e '^/gnu/store/.*-blog-rekahsoft-ca-.*' | xargs guix gc -D
Enhanced Watch
When making adjustments to the site builder itself, it is useful to have functionality
similar to the site content watching feature of Hakyll, but for haskell source files.
Luckily, ghcid can be used for this, and is included in the projects development
dependencies, specified in the guix.scm
file.
ghcid --test _devWatch
Building a Release
The software built that itself builds this blog is released as a Guix package. It is currently not, and is not ever expected to be distributed via a channel, as it provides little benefit to anyone except myself, and is meant to operate along with stateful data, including the site templates, content, pages, posts, etc..
To build a release, run the following command:
guix time-machine -C channels.scm -- build -f guix.scm
This will produce a guix package with the following three outputs:
-
out
- The
blog-rekahsoft-ca
site builder (also available assite
), andgencss
css generator binaries -
site
- A build of the website made with the site builder, etc.. in the
out
output of this package, using the content at the same version -
static
- License file and any other file that should be distributed (eg manual)
Verifying a Release
To manually verify a release, any http webserver can be used to serve the site
output of
the guix build. For instance, this is how Python's http.server
builtin http server can be
used.
guix shell python-wrapper -- python -m http.server -d $(guix time-machine -C channels.scm -- build -f guix.scm | grep -E '^.*-site') 3000
TODO What is done with the release?
Deploying the Site
Terraform is used to deploy this site. Its configuration files are located in ./infra
.
Under normal conditions, all deployments occur from my internal ci/cd system. This ensures that the deployment process is reliable, repeatable and quick. However, in the case of both development and emergency deployments, clear documentation surrounding the deployment process is necessary.
Start Deployment Environment
Setup a Particular Environment
Three environments (terraform workspaces) are currently available, including:
- default
- unused default terraform workspace
- staging
- https://www.blog.staging.rekahsoft.ca
- production
- https://www.blog.rekahsoft.ca
make setup ENV=<env>
From this point onward, any make
target run will operate on the selected environment,
unless its switched with the workspace
or setup
targets, or manually with terraform
.
See What Infrastructure Will Change
Run a terraform plan to see how the selected environments infrastructure will change.
make plan
Deploy the Site
Run a terraform apply to deploy to the selected environment.
make deploy
Working with Terraform Directly
Within a development environment, terraform
, its providers and all other dependencies are
available. As such, its possible to directly leverage terraform
and its various operations.
This is particularly useful when debugging or adding make targets.
TODO Writing a Blog Post
The most natural way to edit and preview a post is to use direnv along with this repository,
which uses guix shell
to transparently provide all necessary tools, including Hakyll Site
Commands. When using direnv, a containerized environment will not be used, however for
content development, this is not a concern.
guix time-machine -C channels.scm -- shell -CN -E LANG -E TERM -f guix.scm
DOING Vendor external libraries using Guix
Some …
[ -h lib/MathJax ] && rm lib/MathJax
[ -e lib/MathJax ] && echo "lib/MathJax exists, but not as a symlink; please manually remove it!" && exit 1
ln -s $(guix time-machine -C channels.scm -- shell -Df guix.scm -- bash -c 'echo $GUIX_ENVIRONMENT')/share/javascript/mathjax lib/MathJax
Makefile
In order to simplify running the various commands outlined throughout this document, a
Makefile
is defined below.
# THIS IS A GENERATED FILE, DO NOT EDIT!
# Instead modify README.org appropriately
.DEFAULT_GOAL := watch
.PHONY: bootstrap
bootstrap:
<<bootstrap>>
.PHONY: dev
dev:
./scripts/start-development-environment.sh
.PHONY: dev-deploy
dev-deploy:
./scripts/start-deployment-environment.sh
.PHONY: dev-all
dev-all:
./scripts/start-development-and-deployment-environment.sh
.PHONY: watch-all
watch-all:
./scripts/watch-all.sh
.PHONY: watch
watch:
./scripts/start-development-environment.sh -- <<watch-all>>
.PHONY: build
build-release:
./scripts/build-release.sh
.PHONY: vendor
vendor:
./scripts/vendor-deps.sh
.PHONY: clean
clean:
./scripts/clean-guix-store.sh
rm -rf scripts lib/MathJax Makefile
Continuous Integration & Delivery
TODO
Generate .drone.yaml
drone jsonnet --stream --format
Note: currently drone-cli
is not packaged for Guix, so for the time being, it can be run
with docker as follows, where <version>
is the drone-cli version.
docker run -v ${PWD}:/tmp/app -w /tmp/app --rm -it drone/cli:<versin> jsonnet --stream --format