Compare commits

...

52 Commits

Author SHA1 Message Date
Collin J. Doering 1c5aeba23e
.dron.jsonnet: Reorganize jsonnet; no change to yaml output 2021-12-09 23:03:53 -05:00
Collin J. Doering 7fc316a171
README.org: Update drone status badge after repository move 2021-12-09 20:42:17 -05:00
Collin J. Doering b9ee2f8266
.drone.jsonnet: Addionally wait for pending status during promotion 2021-12-09 20:11:33 -05:00
Collin J. Doering 5c4138d4d4
.drone.jsonnet: Correct promoted pipeline status checking 2021-12-09 20:08:46 -05:00
Collin J. Doering 9b167b4d9d
.drone.jsonnet: Export DRONE_TOKEN so its available in subprocess 2021-12-09 19:37:02 -05:00
Collin J. Doering 880978c9a1
.drone.jsonnet: Use correct site build during ci
This makes the assumption that the guix store is available from all drone-ci nodes
2021-12-09 18:12:57 -05:00
Collin J. Doering f11fbdd3f4
infra/Makefile: Remove CI env var check for terraform workspace switching 2021-12-09 12:50:08 -05:00
Collin J. Doering ec708bcf29
Revert ".drone.jsonnet: TEST - why is the setup step not switching workspaces?"
This reverts commit 5d00301c86.
2021-12-09 12:49:52 -05:00
Collin J. Doering 5d00301c86
.drone.jsonnet: TEST - why is the setup step not switching workspaces? 2021-12-09 12:42:00 -05:00
Collin J. Doering b162752ce6
.drone.jsonnet: Do not use guix shell containerization as it fails within non-privileged docker 2021-12-09 12:17:43 -05:00
Collin J. Doering bc19c03d75
.drone.jsonnet: Fix issue specific to running guix shell/environment via drone-ci 2021-12-09 11:59:51 -05:00
Collin J. Doering 6a00da6970
.drone.jsonnet: Explicitly specify manifest to use for deploySteps 2021-12-09 10:42:47 -05:00
Collin J. Doering 920fe7d9f4
Enhance drone pipeline and switch to jsonnet based configuration
* .drone.yml: This is now a generated file, based on .drone.jsonnet

* .drone.jsonnet: Add jsonnet drone pipeline configuration to replace existing .drone.yml
file. Note, currently this is used to manually generate .drone.yml. I need to determine how
to handle jsonnet libraries effectively as drone's built in jsonnet support does not allow
for importing libraries. In addition to converting to jsonnet, the pipeline was enhanced with
promotion and deployment functionality that has not yet been tested.
2021-12-09 10:34:37 -05:00
Collin J. Doering 5c2f33456e
README.org: Write build and clean sections in development guide 2021-12-07 21:17:32 -05:00
Collin J. Doering 46d25cd912
Remove top-level manifest.scm as package in guix.scm is sufficient
* manifest.scm: Removed in favor of guix.scm and the blog-rekahsoft-ca package definition within it

* guix.scm: Add environment variable PS1 that was formally set by manifest.scm

* README.org (Start Development Environment): Update guix commands used now that there is no top-level manifest.scm
2021-12-07 21:17:29 -05:00
Collin J. Doering 1dae28b6f4
README.org: Improve grammar make README read more clearly 2021-12-07 00:28:45 -05:00
Collin J. Doering b1476560ec
.gitignore: Update terraform entries 2021-12-07 00:24:29 -05:00
Collin J. Doering 0b47b38f61
TODO.org: (Invalid page urls load as the home page): Add new TODO 2021-12-06 23:31:09 -05:00
Collin J. Doering 1e1ae99446
Use guix for managing the required deployment environment
* infra/variables.tf: Add new variable 'site_statis_files_dir'

	* infra/manifest.scm: Add guix manifest that captures all tools required for deploying this site. This currently includes terraform, in use terraform providers, as well as awscliv2  which is used directly from a null resource

	* infra/main.tf: Pin all provider version so they are available from the rekahsoft-guix channel
	Remove the need for the template provider. It is still included as these changes need to be applied to all environments before it can be removed.
	Remove TF-UPGRAGE-TODO's
	Use the new variable 'site_static_files_dir' for the location of the static site files to be deployed

	* channels.scm (channel): Add symlink to top-level channels file

	* infra/Makefile (SELECTED_WORKSPACE): Removed the dependency on terraform
	(clean): Add new PHONY target 'clean' which cleans up terraform temporary files
	(workspace): Add new PHONY target 'workspace which switches to user provided ENV

	* channels.scm (channel): Updated rekahsoft-guix channel

	* README.org (Features): Updated sections on deployment
2021-12-06 22:25:05 -05:00
Collin J. Doering ee09fc99b8
channels.scm: Update guix and rekahsoft-guix 2021-12-05 04:08:14 -05:00
Collin J. Doering 5ba926b5d2
.drone.yml: Remove --with-source option during ci builds 2021-12-01 23:38:33 -05:00
Collin J. Doering 4b18054324
guix.scm: Use git commit in version string 2021-12-01 23:36:59 -05:00
Collin J. Doering 29dc67c881
README.org: Move 'Known Issues' to TODO.org 2021-11-30 23:40:03 -05:00
Collin J. Doering dd6434b4ee
README.org: Update documentation now that site script is included in package 2021-11-30 23:40:00 -05:00
Collin J. Doering fd15b80b62
Use git source instead of tarball for blog-rekahsoft-ca package
* manifest.scm:
* guix.scm: Prefix global variables with '%'; use git source for blog-rekahsoft-ca package
2021-11-30 23:34:19 -05:00
Collin J. Doering ff1d13a670
src/site.hs: Fix compiler warnings about unused modules/redundant imports 2021-11-29 09:48:12 -05:00
Collin J. Doering e69f751011
Include site script in package and build site as another package output 2021-11-29 09:48:03 -05:00
Collin J. Doering 50f14dfa8d
.drone.yml: Remove ci test 2021-11-28 23:20:46 -05:00
Collin J. Doering c9ad841f53
README.org: Update development documentation 2021-11-28 23:19:40 -05:00
Collin J. Doering fc40cc18d1
manifest.scm: Removed unneeded development tools 2021-11-28 14:20:24 -05:00
Collin J. Doering 3ebc0b1bbd
.drone.yml: TEST 2021-11-28 06:43:38 -05:00
Collin J. Doering fc2fe49022
.drone.yml: TEST 2021-11-28 01:42:26 -05:00
Collin J. Doering 5dfb1b88ee
site: Remove test command from site as the test suite was removed in 84735e9a 2021-11-27 23:52:32 -05:00
Collin J. Doering 10a72486a5
.gitignore: Remove .stack 2021-11-27 23:43:57 -05:00
Collin J. Doering d6464b8ac7
Remove application cache functionality now that its deprecated 2021-11-27 23:43:22 -05:00
Collin J. Doering 6c7fa76f4a
templates/default.js: Update MathJax cdn url 2021-11-27 23:41:12 -05:00
Collin J. Doering 8f47caede9
README.org: Include droneci badge directly as html 2021-11-26 21:18:38 -05:00
Collin J. Doering 7d59eb100b
Remove stack.yaml{,.lock} 2021-11-26 08:08:52 -05:00
Collin J. Doering b1875dfc27
Update copyright year/s 2021-11-26 08:08:34 -05:00
Collin J. Doering df02a2a62d
README.org: Add droneci badge and adjust title a last time
Note: there is a bug in gitea rendering of org-mode files which results in additional space
at the top of the document.
2021-11-26 08:07:36 -05:00
Collin J. Doering a1dbf95c49
.drone.yml: Fix reference to renamed file blog-rekahsoft-ca.scm -> guix.scm 2021-11-26 01:51:31 -05:00
Collin J. Doering b40ff4f4ad
README.org: Fix title spacing 2021-11-26 01:46:49 -05:00
Collin J. Doering 2f3d052cd6
README.org: Fix title spacing 2021-11-26 01:41:54 -05:00
Collin J. Doering 7d679dd94b
README.org: Add more known issues and fix title spacing 2021-11-26 01:39:46 -05:00
Collin J. Doering a15def12bc
Replace README.md with README.org 2021-11-25 22:56:24 -05:00
Collin J. Doering d3ae74879b
Move blog-rekahsoft-ca.scm -> guix.scm and update references 2021-11-25 21:21:36 -05:00
Collin J. Doering b22b7e04b4
Fix bug where are javascript files were incorrectly processed
The compressCssCompiler has been used for javascript for a long time for this website.
Despite this not being a wise decision, it worked up until some version of Hakyll. I should
minify the javascript as part of the site building process, but for now will simply copy the
javascript files as is.
2021-11-25 21:17:29 -05:00
Collin J. Doering 2620a711fb
Update jquery from v1.11.2 -> v1.12.3 2021-11-25 21:16:18 -05:00
Collin J. Doering 06e473583a
image-src: Add inkscape svg's that were used to build site graphics 2021-11-25 14:46:18 -05:00
Collin J. Doering b0cb08f624
Move ghc-* packages to the rekahsoft-guix channel
* channels.scm: Update rekahsoft-guix channel
* blog-rekahsoft-ca.scm: Removed ghc-* packages that are now part of rekahsoft-guix channel;
                         these ghc-* packages can be contributed upstream to guix.
2021-11-25 11:49:45 -05:00
Collin J. Doering f691f99210
WIP: Use newly added channel.scm guix channel file for ci 2021-11-24 21:36:10 -05:00
Collin J. Doering 94e74df3be
WIP: guix development workflow 2021-11-23 22:58:24 -05:00
26 changed files with 4460 additions and 2365 deletions

216
.drone.jsonnet Normal file
View File

@ -0,0 +1,216 @@
// See: https://github.com/kradalby/drone-jsonnet/blob/master/drone.libsonnet
local droneStatus = ['success', 'failure'];
local ci = {
pipeline:: {
new()::
self.withKind("pipeline"),
withName(name)::
self + { name: name },
withKind(kind)::
self + { kind: kind },
withType(type)::
self + { type: type },
withNode(node)::
self + { node: node },
withTrigger(trigger)::
self + { trigger: trigger },
withDependsOn(n)::
self + { depends_on: n },
withNodeSelector(ns)::
self + { node_selector: ns },
withSteps(steps)::
self + if std.type(steps) == 'array'
then { steps: steps }
else { steps: [steps] },
step:: {
new(name='', image='')::
self.withName(name).withImage(image).withPullIfNotExists(),
withName(name)::
self + { name: name },
withImage(image)::
self + if image != '' then { image: image } else {},
withAlwaysPull()::
self + { pull: 'always' },
withPullIfNotExists()::
self + { pull: 'if-not-exists' },
withCommands(commands)::
self + if std.type(commands) == 'array'
then { commands: commands }
else { commands: [commands] },
withTrigger(trigger)::
self + { trigger: trigger }, // TODO: this is duplicated in pipeline object
withEnv(envs)::
self + { environment+: envs },
withRuntimeEnvVar(envs)::
local existingCmds = if std.objectHas(self, "commands") then self.commands else [];
self + {
commands: std.map(function (i) std.format('export %s="%s"', [i, envs[i]]),
std.objectFields(envs)) + existingCmds
},
withWhen(when)::
self + { when: when },
withSettings(settings)::
self + { settings: settings },
},
when:: {
new()::
self + {},
withBranch(branch)::
self + if std.type(branch) == 'array'
then { branch: branch }
else { branch: [branch] },
withEvent(e)::
self + if std.type(e) == 'array'
then { event: e }
else { event: [e] },
withStatus(s)::
self + if std.type(s) == 'array'
then { status: s }
else { status: [s] },
withStatusAll()::
self.withStatus(droneStatus),
},
},
trigger:: {
new()::
self + {},
withBranch(branch)::
self + if std.type(branch) == 'array'
then { branch: branch }
else { branch: [branch] },
withEvent(e)::
self + if std.type(e) == 'array'
then { event: e }
else { event: [e] },
withStatus(s)::
self + if std.type(s) == 'array'
then { status: s }
else { status: [s] },
withStatusAll()::
self.withStatus(droneStatus),
},
env_from_secret(dict):: {
[key]: {
from_secret: dict[key],
}
for key in std.objectFields(dict)
},
promoteStep(env,
secret_name_drone_token="drone_token",
image="docker.nexus.home.rekahsoft.ca/drone/cli:1.4-alpine")::
local dronePromoteCmd(env) = [
"export DRONE_SERVER=\"${DRONE_SYSTEM_PROTO}://${DRONE_SYSTEM_HOST}\"",
"export DRONE_TOKEN",
std.format('DRONE_PROMOTED_PIPELINE_ID=$(drone build promote --format \'{{ .Number }}\' "$DRONE_REPO" "$DRONE_BUILD_NUMBER" "%s")', env),
'while status="$(drone build info --format \'{{ .Status }}\' $DRONE_REPO $DRONE_PROMOTED_PIPELINE_ID)"; do
case "$status" in
pending|running)
sleep 30s
;;
success)
break
;;
failure|error|killed)
echo "Promoted job with id $DRONE_PROMOTED_PIPELINE_ID failed with status \'$status\'."
exit 1
;;
*)
echo "Unknown pipeline status \'$status\'."
exit 1
esac
done',
];
ci.pipeline.step.new(std.format("promote-%s", env), image)
.withWhen(ci.pipeline.when.new()
.withBranch("master")
.withEvent("push"))
.withCommands(dronePromoteCmd(env))
.withEnv(ci.env_from_secret({
DRONE_TOKEN: "drone_token"
}))
};
local guix_pipeline(name) = ci.pipeline.new()
.withName(name)
.withType("docker")
.withNode({ "guix": "on"});
local guix_step(name,
commands,
image="docker.nexus.home.rekahsoft.ca/guix:latest") =
ci.pipeline.step.new(name, image).withPullIfNotExists().withCommands(commands);
local guix_step_time_machine(name,
commands,
cwd=".",
channels="channels.scm",
image="docker.nexus.home.rekahsoft.ca/guix:latest") =
ci.pipeline.step.new(name, image).withPullIfNotExists().withCommands(
// Conditionally change directory
(if cwd == "."
then [] else [std.format("cd %s", cwd)]) +
// Drone-ci does not populate a /etc/passwd which causes issues with guix
["echo 'root:x:0:0:root:/root:/bin/bash' >> /etc/passwd"] +
// Expand provide guix commands into executable shell
std.map(function(i) std.format("guix time-machine -C %s -- %s", [channels, i]),
if std.type(commands) == 'array' then commands else [commands]));
local deployStep(name, target=name, args=[]) = guix_step_time_machine(
name,
std.format('shell -m manifest.scm -- make %s ENV="${DRONE_DEPLOY_TO}" %s', [target, std.join(" ", args)]),
cwd="infra",
channels="../channels.scm")
.withEnv({ PLAN: "out.plan" } + ci.env_from_secret({
AWS_ACCESS_KEY_ID: "aws_access_key_id",
AWS_SECRET_ACCESS_KEY: "aws_secret_access_key",
}));
[
guix_pipeline("validate").withTrigger(ci.trigger.new().withEvent(["push", "pull_request", "tag"])).withSteps([
guix_step_time_machine("build", "build -f guix.scm"),
ci.promoteStep("staging"),
ci.promoteStep("production"),
]),
guix_pipeline("deploy").withTrigger(ci.trigger.new().withEvent("promote")).withSteps([
deployStep("init", "setup"),
deployStep("plan").withRuntimeEnvVar({
TF_VAR_site_static_files_dir: "$(guix time-machine -C channels.scm -- build -f guix.scm | grep -e '^.*-site$')"
}),
deployStep("deploy"),
])
]

View File

@ -1,12 +1,121 @@
---
kind: pipeline
type: docker
name: blog-rekahsoft-ca
name: validate
workspace:
path: /drone/blog-rekahsoft-ca
platform:
os: linux
arch: amd64
steps:
- name: build-site
image: docker.nexus.home.rekahsoft.ca/fpco/stack-build:lts-12.0
- name: build
pull: if-not-exists
image: docker.nexus.home.rekahsoft.ca/guix:latest
commands:
- ./site build
- echo 'root:x:0:0:root:/root:/bin/bash' >> /etc/passwd
- guix time-machine -C channels.scm -- build -f guix.scm
- name: promote-staging
pull: if-not-exists
image: docker.nexus.home.rekahsoft.ca/drone/cli:1.4-alpine
commands:
- export DRONE_SERVER="${DRONE_SYSTEM_PROTO}://${DRONE_SYSTEM_HOST}"
- export DRONE_TOKEN
- DRONE_PROMOTED_PIPELINE_ID=$(drone build promote --format '{{ .Number }}' "$DRONE_REPO" "$DRONE_BUILD_NUMBER" "staging")
- "while status=\"$(drone build info --format '{{ .Status }}' $DRONE_REPO $DRONE_PROMOTED_PIPELINE_ID)\"; do\ncase \"$status\" in\n pending|running)\n sleep 30s\n ;;\n success)\n break\n ;;\n failure|error|killed)\n echo \"Promoted job with id $DRONE_PROMOTED_PIPELINE_ID failed with status '$status'.\"\n exit 1\n ;;\n *)\n echo \"Unknown pipeline status '$status'.\"\n exit 1\nesac\ndone"
environment:
DRONE_TOKEN:
from_secret: drone_token
when:
branch:
- master
event:
- push
- name: promote-production
pull: if-not-exists
image: docker.nexus.home.rekahsoft.ca/drone/cli:1.4-alpine
commands:
- export DRONE_SERVER="${DRONE_SYSTEM_PROTO}://${DRONE_SYSTEM_HOST}"
- export DRONE_TOKEN
- DRONE_PROMOTED_PIPELINE_ID=$(drone build promote --format '{{ .Number }}' "$DRONE_REPO" "$DRONE_BUILD_NUMBER" "production")
- "while status=\"$(drone build info --format '{{ .Status }}' $DRONE_REPO $DRONE_PROMOTED_PIPELINE_ID)\"; do\ncase \"$status\" in\n pending|running)\n sleep 30s\n ;;\n success)\n break\n ;;\n failure|error|killed)\n echo \"Promoted job with id $DRONE_PROMOTED_PIPELINE_ID failed with status '$status'.\"\n exit 1\n ;;\n *)\n echo \"Unknown pipeline status '$status'.\"\n exit 1\nesac\ndone"
environment:
DRONE_TOKEN:
from_secret: drone_token
when:
branch:
- master
event:
- push
node:
guix: on
trigger:
event:
- push
- pull_request
- tag
---
kind: pipeline
type: docker
name: deploy
platform:
os: linux
arch: amd64
steps:
- name: init
pull: if-not-exists
image: docker.nexus.home.rekahsoft.ca/guix:latest
commands:
- cd infra
- echo 'root:x:0:0:root:/root:/bin/bash' >> /etc/passwd
- "guix time-machine -C ../channels.scm -- shell -m manifest.scm -- make setup ENV=\"${DRONE_DEPLOY_TO}\" "
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: aws_secret_access_key
PLAN: out.plan
- name: plan
pull: if-not-exists
image: docker.nexus.home.rekahsoft.ca/guix:latest
commands:
- export TF_VAR_site_static_files_dir="$(guix time-machine -C channels.scm -- build -f guix.scm | grep -e '^.*-site$')"
- cd infra
- echo 'root:x:0:0:root:/root:/bin/bash' >> /etc/passwd
- "guix time-machine -C ../channels.scm -- shell -m manifest.scm -- make plan ENV=\"${DRONE_DEPLOY_TO}\" "
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: aws_secret_access_key
PLAN: out.plan
- name: deploy
pull: if-not-exists
image: docker.nexus.home.rekahsoft.ca/guix:latest
commands:
- cd infra
- echo 'root:x:0:0:root:/root:/bin/bash' >> /etc/passwd
- "guix time-machine -C ../channels.scm -- shell -m manifest.scm -- make deploy ENV=\"${DRONE_DEPLOY_TO}\" "
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: aws_secret_access_key
PLAN: out.plan
node:
guix: on
trigger:
event:
- promote
...

9
.gitignore vendored
View File

@ -7,7 +7,6 @@
# Haskell
*.o
*.hi
.stack
# Hakyll
_site
@ -15,7 +14,7 @@ _cache
dist
# Terraform
.terraform
terraform.tfstate.d
*.local.tfvars
*.plan
infra/.terraform
infra/terraform.tfstate.d
infra/*.local.tfvars
infra/*.plan

View File

@ -1,6 +1,6 @@
Terms of Use
This site is Copyright 2016 © - Collin Doering and is distributed under
This site is Copyright 2016-2021 © - Collin Doering and is distributed under
the following terms.
1. All rights reserved on the "#! λ Slang" name, as well as on the

View File

@ -1,88 +0,0 @@
# Source Code for **[#! Lambda Slang](http://www.blog.rekahsoft.ca)**
* [Features](#features)
* [Tools](#tools)
* [License](#license)
* [Building](#building)
* [Deploying](#deploying)
* [Issues](#issues)
[#! Lambda Slang](http://www.blog.rekahsoft.ca) is the personal technical blog of *Collin Doering*,
built using software that [respects our freedoms](https://www.gnu.org/philosophy/free-sw.html).
## Features <a name="features"></a>
* [Single Page Application (SPA)](http://en.wikipedia.org/wiki/Single-page_application)
* Utilizes CSS 3
* Uses HTML5 Application Cache for offline viewing of website
## Tools <a name="tools"></a>
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
* [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 <a name="license"></a>
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.
## Building <a name="building"></a>
[Stack][] is used to manage dependencies for this project. A simple wrapper script `site` is
provided that also takes care of building the static site and offering access to hakyll
commands.
$ ./site build
$ ./site watch
## Deploying <a name="deploying"></a>
Terraform is used to deploy this site. Its configuration files are located in `./infra`. Three
workspaces are currently available, including:
- default (unused)
- staging
- production
For example, this is how to deploy the production version of the site:
$ cd infra
$ terraform workspace select production
$ terraform plan --var-file=production.tfvars --out local.plan
$ terraform apply local.plan
## Issues <a name="issues"></a>
If you have an issue while browsing [my blog](http://www.blog.rekahsoft.ca) please file a issue
in the [blog-rekahsoft-ca](https://git.rekahsoft.ca/rekahsoft/blog-rekahsoft-ca/issues) issue
tracker.
[Hakyll]: http://jaspervdj.be/hakyll/
[Clay]: http://fvisser.nl/clay/
[Skeleton]: http://www.getskeleton.com/
[JQuery]: http://jquery.com
[JQuery-address]: https://github.com/asual/jquery-address
[MathJax]: http://www.mathjax.org/
[Inkscape]: http://inkscape.org/
[Gimp]: http://www.gimp.org/
[Stack]: https://haskellstack.org
[Gnu Emacs]: http://www.gnu.org/software/emacs/
[Gnu Free Fonts]: http://www.gnu.org/software/freefont/
[GPLv3]: https://www.gnu.org/licenses/gpl.html
[Creative Commons BY-NC-SA License]: http://creativecommons.org/licenses/by-nc-sa/4.0/
[Creative Commons BY-NC-ND License]: http://creativecommons.org/licenses/by-nc-nd/4.0/

232
README.org Normal file
View File

@ -0,0 +1,232 @@
#+TITLE: Source Code for [[http://www.blog.rekahsoft.ca][#! Lambda Slang]]
#+AUTHOR: Collin J. Doering
#+BEGIN_EXPORT html
<p><a href="https://ci.home.rekahsoft.ca/rekahsoft-public/blog-rekahsoft-ca"><img src="https://ci.home.rekahsoft.ca/api/badges/rekahsoft-public/blog-rekahsoft-ca/status.svg" alt="Build Status"></a></p>
#+END_EXPORT
#+begin_abstract
[[http://www.blog.rekahsoft.ca][#! Lambda Slang]] is the personal technical blog of *Collin Doering*, built using software that
[[https://www.gnu.org/philosophy/free-sw.html][respects our freedoms]].
#+end_abstract
* Features
- [[http://en.wikipedia.org/wiki/Single-page_application][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:
- [[http://jaspervdj.be/hakyll/][Hakyll]] is used to generate site from static files
- [[http://fvisser.nl/clay/][Clay]] is used for CSS pre-processing
- [[http://www.getskeleton.com/][Skeleton]] is used for CSS boilerplate
- [[http://www.mathjax.org/][MathJax]] is used for rendering mathematics
- [[http://jquery.com][JQuery]] and [[https://github.com/asual/jquery-address][JQuery-address]] are used for various DOM manipulations
- [[https://guix.gnu.org/][Gnu Guix]] is used to manage development environments and packaging
- [[http://inkscape.org/][Inkscape]] and the [[http://www.gimp.org/][Gimp]] were used to create various images/artwork
- [[http://www.gnu.org/software/freefont/][Gnu Free Fonts]], specifically *FreeMono* is used as main font
- [[http://www.gnu.org/software/emacs/][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 ([[https://www.gnu.org/licenses/gpl.html][GPLv3]], [[http://creativecommons.org/licenses/by-nc-sa/4.0/][Creative Commons BY-NC-SA License]], and [[http://creativecommons.org/licenses/by-nc-nd/4.0/][Creative Commons BY-NC-ND
License]]) are deployed dependent on which part of the site is in question. Please see the
[[./LICENSE][LICENSE]] file for full details.
* TODO Guix Development Environment
:PROPERTIES:
:header-args: :session *vterm blog-rekahsoft-ca* :results none
:END:
[[https://guix.gnu.org/][Gnu Guix]] is used to package this project and manage its dependencies, as well as to provide
reproducible development environments.
A simple wrapper script [[./site][site]] is provided that wraps the hakyll powered site builder to
offer some additional functionality.
** Start Development Environment
The development environment is defined by the following files:
- [[./channels.scm][channels.scm]] :: Specifically defines a set of available software, their versions and their build recipe.
- [[./guix.scm][guix.scm]] :: Defines the package for this site, ~blog-rekahsoft-ca~.
To start a development environment, run the following:
#+begin_src sh
guix time-machine -C channels.scm -- shell -CN -E '^LANG$' -E '^TERM$' -E '^PS1$' -f guix.scm -Df guix.scm
#+end_src
This uses the [[info:guix#Invoking guix time-machine][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 [[info:guix#Invoking guix shell][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. Finally, ~-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 Development Environment
[[https://guix.gnu.org/][Gnu Guix]] is used, similar to in the [[*Start Development Environment][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][infra/channels.scm]] :: Symlink to [[./channels.scm][../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][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][infra/manifest.scm]] :: Defines packages required for deployment of this site
To start a deployment development environment, run the following:
#+begin_src sh
cd infra
guix time-machine -C channels.scm -- shell -CN -E '^LANG$' -E '^TERM$' -E '^PS1$' -E '^AWS.*$'
#+end_src
*** Composing Site Development and Deployment Environments
#+begin_src sh
guix time-machine -C channels.scm -- shell -CN -E '^LANG$' -E '^TERM$' -E '^PS1$' -E '^AWS.*$' -f guix.scm -Df guix.scm -m infra/manifest.scm
#+end_src
** 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. Under usual conditions it is not invoked directly, but
instead via the ~site~ wrapper script. For example, this is how within a development
environment the site can be built.
#+begin_src sh
site build
#+end_src
** Clean Site
[[*Build 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:
#+begin_src sh
site clean
#+end_src
** Watch
#+begin_src sh
site watch
#+end_src
** Deploy 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.
*** TODO ~site deploy~ command
#+begin_src sh
site deploy
#+end_src
*** Start [[*Deployment Development Environment][Deployment Development 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
#+begin_src sh
make setup ENV=<env>
#+end_src
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.
#+begin_src sh
make plan
#+end_src
*** Deploy the Site
Run a terraform apply to deploy to the selected environment.
#+begin_src sh
make deploy
#+end_src
*** 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.
** Clean up Guix Store
#+begin_src sh
guix gc --list-dead | grep -e '^/gnu/store/.*-blog-rekahsoft-ca-.*' | xargs guix gc -D
#+end_src
* 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:
#+begin_src sh
guix time-machine -C channels.scm -- build -f guix.scm
#+end_src
This will produce a guix package with the following three outputs:
- ~out~ :: The ~blog-rekahsoft-ca~ site builder and ~gencss~ css generator binaries, as well
as ~site~ user script
- ~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)
** TODO What is done with the release?
* Writing a Blog Post
#+begin_src sh
guix time-machine -C channels.scm -- shell -CN -E LANG -E TERM -E PS1 -f guix.scm -- site watch
#+end_src
* Known Issues
If you have an issue while browsing [[http://www.blog.rekahsoft.ca][my blog]] please file a issue in the [[https://git.rekahsoft.ca/rekahsoft/blog-rekahsoft-ca/issues][blog-rekahsoft-ca]]
issue tracker.
To see a list of already known issues, see [[./TODO.org][TODO.org]].

41
TODO.org Normal file
View File

@ -0,0 +1,41 @@
#+TITLE: blog-rekahsoft-ca TODO's
#+AUTHOR: Collin J. Doering
* DONE Remove the use of application cache now that most major browsers are pulling support
CLOSED: [2021-11-28 Sun 10:43]
- State "DONE" from "TODO" [2021-11-28 Sun 10:43]
* TODO The image full screen features does not work on initial page loads and requires going to at least one other page :bug:
See:
- [[file:js/default.js::// Add fullscreen functionality to inline-images and figures][Fullscreen image onclik functionality is added in the router upon content loading, which doesn't occur on the intial page load]]
* TODO Completely replace all references to stack
* TODO Fix analytics (pywiki is no longer in use, find and use something else)
* TODO Modernize javascript in [[file:js/default.js][js/default.js]]
* TODO Allow posts (and pages?) to be written in org-mode
See:
- https://github.com/jaspervdj/hakyll/issues/700
- https://turbomack.github.io/posts/2016-12-21-org-mode-in-hakyll.html
* TODO Fix rendering of mathjax
* TODO Use javascript/css sources from guix packages instead of vendoring them or using them from the internet
- [ ] jQuery
- [ ] mathjax
- [ ] skeleton
* TODO Setup automated deployment from ci
* TODO Use separate file for terraform backend
Used to be: +Add missing terraform backend files to repository+, however this was not true.
The backend is just embedded in [[./infra/main.tf][infra/main.tf]].
* TODO Figure out fast workflow for modifying haskell sources without rebuilding guix package
*** TODO [[./clay][clay sources]]
*** TODO [[./src][hakell sources]]
* TODO Allow external code files to be included in pages and blog entries
See: http://blog.tpleyer.de/posts/2019-04-21-external-code-inclusion-with-hakyll.html
* TODO Ensure [[./blog-rekahsoft-ca.cabal][blog-rekahsoft-ca.cabal]] is usable with cabal and has the correct dependencies
* TODO Build the site as a guix package output of ~blog-rekahsoft-ca~
* TODO Invalid page urls load as the home page
Eg. https://blog.rekahsoft.ca/this-is-not-a-valid-page.html will show the home page, and
retain this url. However, it should show a page not found page.

View File

@ -61,8 +61,8 @@ executable blog-rekahsoft-ca
other-extensions: OverloadedStrings, TupleSections, FlexibleContexts
-- Other library packages from which modules are imported.
build-depends: base >=4.11 && <4.12,
hakyll >= 4.12 && <4.13,
build-depends: base >=4.14 && <4.15,
hakyll >= 4.15 && <4.16,
pandoc >= 1.13,
parsec >= 3.1,
filepath >= 1.3,
@ -89,7 +89,7 @@ executable gencss
other-extensions: OverloadedStrings
-- Other library packages from which modules are imported.
build-depends: base >=4.11 && <4.12,
build-depends: base >=4.14 && <4.15,
clay >=0.13 && <0.14,
text >=1.2 && <1.3

20
channels.scm Normal file
View File

@ -0,0 +1,20 @@
(list (channel
(name 'guix)
(url "https://git.savannah.gnu.org/git/guix.git")
(commit
"15404ff19b304bc9efcdb9a326318fb9fa9716da")
(introduction
(make-channel-introduction
"9edb3f66fd807b096b48283debdcddccfea34bad"
(openpgp-fingerprint
"BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA"))))
(channel
(name 'rekahsoft-guix)
(url "https://git.rekahsoft.ca/rekahsoft/rekahsoft-guix.git")
(commit
"9915757715c96e0643bd5bd0dff466a1d9c652a7")
(introduction
(make-channel-introduction
"191cdaa0947657e0c85fe89ebbb8e7b1e7a8e0a4"
(openpgp-fingerprint
"F8D5 46F3 AF37 EF53 D1B6 48BE 7B4D EB93 212B 3022")))))

77
guix.scm Normal file
View File

@ -0,0 +1,77 @@
;; (C) Copyright Collin J. Doering 2021
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; File: blog-rekahsoft-ca.scm
;; Author: Collin J. Doering <collin.doering@rekahsoft.ca>
;; Date: Nov 21, 2021
(use-modules
((guix licenses) #:prefix license:)
(guix packages)
(guix build-system haskell)
(guix git-download)
(guix gexp)
(gnu packages base)
(rekahsoft-gnu packages haskell-web)
(git))
(setenv "PS1" "\\W [env]\\$ ")
(define %srcdir
(dirname (current-filename)))
(define %blog-rekahsoft-ca
(let ((commit (oid->string
(reference-target
(repository-head (repository-open (getcwd))))))
(revision "1"))
(package
(name "blog-rekahsoft-ca")
(version (git-version "0.0.0.0" revision commit))
(source (local-file "." "blog-rekahsoft-ca-git-checkout"
#:recursive? #t
#:select? (git-predicate %srcdir)))
(build-system haskell-build-system)
(native-inputs `(("glibc-utf8-locales" ,glibc-utf8-locales)))
(inputs `(("ghc-hakyll" ,ghc-hakyll)
("ghc-clay" ,ghc-clay)))
(outputs '("out" "site" "static"))
(arguments
`(#:phases
(modify-phases %standard-phases
(add-after 'install 'install-site-script
(lambda* (#:key outputs #:allow-other-keys)
(let ((out (assoc-ref outputs "out")))
(setenv "PATH" (string-append out "/bin:" (getenv "PATH")))
(install-file "site" (string-append out "/bin/"))
#t)))
(add-after 'install-site-script 'build-site
(lambda* (#:key outputs #:allow-other-keys)
(let* ((out (assoc-ref outputs "out"))
(site (assoc-ref outputs "site")))
;; All source files are read-only and need to be adjusted to allow the
;; site to be generated at the end of the build
(for-each make-file-writable (find-files "."))
(invoke "site" "build")
(copy-recursively "_site" site)
#t))))))
(home-page "http://git.rekahsoft.ca/rekahsoft/blog-rekahsoft-ca")
(synopsis "Code, templates and content for my Hakyll powered blog at blog.rekahsoft.ca")
(description
"The code, templates and content for my Hakyll powered blog at blog.rekahsoft.ca.")
(license license:gpl3))))
%blog-rekahsoft-ca

122
images-src/page-next.svg Normal file
View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="55.391273"
height="69.249252"
id="svg6166"
version="1.1"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)"
sodipodi:docname="page-next.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"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs6168">
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow1Lstart"
style="overflow:visible">
<path
id="path8766"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(0.8,0,0,0.8,10,0)"
inkscape:connector-curvature="0" />
</marker>
<filter
inkscape:collect="always"
id="filter9710"
color-interpolation-filters="sRGB"
x="-0.058799997"
y="-0.006681818"
width="1.1176"
height="1.0133636">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.2625"
id="feGaussianBlur9712" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="-276.5"
inkscape:cy="-116"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
showborder="true"
inkscape:showpageshadow="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="944"
inkscape:window-height="1046"
inkscape:window-x="5"
inkscape:window-y="1103"
inkscape:window-maximized="0"
inkscape:pagecheckerboard="0" />
<metadata
id="metadata6171">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-469.66731,-534.0202)">
<g
id="g9738">
<g
transform="matrix(-1.4120922,1.4120922,-1.4120922,-1.4120922,1625.951,-48.42554)"
id="g5873"
style="stroke:none;display:inline">
<path
inkscape:connector-curvature="0"
id="path6537-5-2-1-9"
d="m 635.53312,174.02289 c -8.17336,0 -16.34673,0 -24.52009,0 0,8.17336 0,16.34673 0,24.52009 8.17336,-8.17336 16.34673,-16.34673 24.52009,-24.52009 z"
style="opacity:0.73443986;fill:#000000;fill-opacity:1;stroke:none;display:inline" />
<path
inkscape:connector-curvature="0"
id="path6537-5-2-1-4-28"
d="m 636.56876,177.86442 c -7.14409,0 -14.28819,0 -21.43228,0 0,7.14409 0,14.28818 0,21.43227 7.14409,-7.14409 14.28819,-14.28818 21.43228,-21.43227 z"
style="opacity:0.73443986;fill:#000000;fill-opacity:1;stroke:none;display:inline" />
<path
inkscape:connector-curvature="0"
id="path6537-5-2-1-4-2-3"
d="m 636.78931,182.05502 c -5.96778,0 -11.93557,0 -17.90335,0 0,5.96778 0,11.93556 0,17.90334 5.96778,-5.96778 11.93557,-11.93556 17.90335,-17.90334 z"
style="opacity:0.73443986;fill:#000000;fill-opacity:1;stroke:none;display:inline" />
</g>
<rect
transform="matrix(0.71573023,0,0,0.64570643,133.51222,207.36881)"
y="512.36218"
x="535.71429"
height="94.285713"
width="10.714286"
id="rect9704"
style="opacity:0.7;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;filter:url(#filter9710)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -5,16 +5,17 @@
.PHONY: default
default: deploy
SELECTED_WORKSPACE := $(shell terraform workspace show)
SELECTED_WORKSPACE := $(shell cat .terraform/environment 2>/dev/null || echo default)
ENV := $(if $(ENV),$(ENV),$(SELECTED_WORKSPACE))
.PHONY: setup
setup:
setup: init workspace
.PHONY: workspace
workspace:
ifneq ($(SELECTED_WORKSPACE),$(ENV))
ifndef CI
@terraform workspace select $(ENV)
endif
endif
.PHONY: init
init:
@ -38,3 +39,7 @@ destroy: setup
@terraform destroy \
$(if $(ENV),--var-file=$(ENV).tfvars) \
$(ARGS)
.PHONY: clean
clean:
@rm -rf .terraform

1
infra/channels.scm Symbolic link
View File

@ -0,0 +1 @@
../channels.scm

View File

@ -11,7 +11,7 @@ terraform {
provider "aws" {
region = var.region
version = "~> 2.15"
version = "= 2.70.0"
assume_role {
role_arn = var.workspace_iam_roles[terraform.workspace]
@ -21,7 +21,7 @@ provider "aws" {
provider "aws" {
alias = "us_east_1"
region = "us-east-1"
version = "~> 2.1"
version = "= 2.70.0"
assume_role {
role_arn = var.workspace_iam_roles[terraform.workspace]
@ -33,11 +33,11 @@ provider "null" {
}
provider "random" {
version = "~> 2.1"
version = "= 2.1.2"
}
provider "template" {
version = "~> 2.1"
version = "= 2.2.0"
}
#
@ -55,18 +55,41 @@ locals {
naked_domain = "${local.subdomain}${var.dns_apex}"
domain = "${local.www}${local.naked_domain}"
project_env = "${var.project}-${terraform.workspace}"
bucket_arn = aws_s3_bucket.static.arn
user_arn = aws_iam_user.app_deploy.arn
cloudfront_arn = aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn
}
#
# Data Sources
data "template_file" "s3_origin_policy" {
template = file("templates/s3_origin_policy.json")
data "aws_iam_policy_document" "s3_origin_policy" {
statement {
principals {
type = "AWS"
identifiers = [local.cloudfront_arn]
}
actions = ["s3:GetObject"]
resources = ["${local.bucket_arn}/*"]
}
vars = {
bucket_arn = aws_s3_bucket.static.arn
user_arn = aws_iam_user.app_deploy.arn
cloudfront_arn = aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn
statement {
principals {
type = "AWS"
identifiers = [local.user_arn]
}
actions = ["s3:ListBucket"]
resources = ["${local.bucket_arn}"]
}
statement {
principals {
type = "AWS"
identifiers = [local.user_arn]
}
actions = ["s3:*"]
resources = ["${local.bucket_arn}/*"]
}
}
@ -93,14 +116,6 @@ resource "aws_route53_record" "cert_validation" {
name = aws_acm_certificate.cert.domain_validation_options[count.index]["resource_record_name"]
type = aws_acm_certificate.cert.domain_validation_options[count.index]["resource_record_type"]
ttl = 60
# TF-UPGRADE-TODO: In Terraform v0.10 and earlier, it was sometimes necessary to
# force an interpolation expression to be interpreted as a list by wrapping it
# in an extra set of list brackets. That form was supported for compatibilty in
# v0.11, but is no longer supported in Terraform v0.12.
#
# If the expression in the following list itself returns a list, remove the
# brackets to avoid interpretation as a list of lists. If the expression
# returns a single list item then leave it as-is and remove this TODO comment.
records = [aws_acm_certificate.cert.domain_validation_options[count.index]["resource_record_value"]]
}
@ -210,7 +225,7 @@ resource "aws_route53_record" "static_redirect_ipv6" {
resource "aws_s3_bucket_policy" "static_policy" {
bucket = aws_s3_bucket.static.id
policy = data.template_file.s3_origin_policy.rendered
policy = data.aws_iam_policy_document.s3_origin_policy.json
}
resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
@ -246,14 +261,6 @@ resource "aws_cloudfront_distribution" "cdn" {
bucket = aws_s3_bucket.static_logs.bucket_domain_name
}
# TF-UPGRADE-TODO: In Terraform v0.10 and earlier, it was sometimes necessary to
# force an interpolation expression to be interpreted as a list by wrapping it
# in an extra set of list brackets. That form was supported for compatibilty in
# v0.11, but is no longer supported in Terraform v0.12.
#
# If the expression in the following list itself returns a list, remove the
# brackets to avoid interpretation as a list of lists. If the expression
# returns a single list item then leave it as-is and remove this TODO comment.
aliases = [local.domain]
default_cache_behavior {
@ -340,14 +347,6 @@ resource "aws_cloudfront_distribution" "cdn_redirect" {
bucket = aws_s3_bucket.static_logs.bucket_domain_name
}
# TF-UPGRADE-TODO: In Terraform v0.10 and earlier, it was sometimes necessary to
# force an interpolation expression to be interpreted as a list by wrapping it
# in an extra set of list brackets. That form was supported for compatibilty in
# v0.11, but is no longer supported in Terraform v0.12.
#
# If the expression in the following list itself returns a list, remove the
# brackets to avoid interpretation as a list of lists. If the expression
# returns a single list item then leave it as-is and remove this TODO comment.
aliases = [local.naked_domain]
default_cache_behavior {
@ -404,7 +403,7 @@ aws configure --profile ${aws_iam_user.app_deploy.name} set aws_secret_access_ke
aws configure --profile ${aws_iam_user.app_deploy.name} set region ${var.region};
: Sync latest app build to s3 bucket;
aws --profile ${aws_iam_user.app_deploy.name} s3 sync --delete ../_site s3://${aws_s3_bucket.static.id}/;
aws --profile ${aws_iam_user.app_deploy.name} s3 sync --delete ${var.site_static_files_dir} s3://${aws_s3_bucket.static.id}/;
: Cleanup temporary aws config and credentials files
rm $${AWS_CONFIG_FILE} $${AWS_SHARED_CREDENTIALS_FILE};

37
infra/manifest.scm Normal file
View File

@ -0,0 +1,37 @@
;; (C) Copyright Collin J. Doering 2021
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; File: manifest.scm
;; Author: Collin J. Doering <collin.doering@rekahsoft.ca>
;; Date: Dec 2, 2021
(use-modules
(gnu packages)
(guix packages)
(guix profiles))
(setenv "PS1" "\\W [env]\\$ ")
(setenv "AWS_PAGER" "")
(specifications->manifest
`("coreutils"
"make"
"terraform-wrapper@0.12.31"
"terraform-provider-aws"
"terraform-provider-null@2.1.2"
"terraform-provider-random"
"terraform-provider-template"
"awscliv2"
"nss-certs"))

View File

@ -25,3 +25,6 @@ variable "enable_naked_domain" {
default = false
}
variable "site_static_files_dir" {
default = "../_site"
}

View File

@ -345,17 +345,7 @@ var _paq = _paq || [];
status: status
};
function appCacheUpdateReady () {
window.applicationCache.swapCache();
// TODO: find what resource is loaded currently and reload it if it has changed
}
function init() {
window.addEventListener("updateready", appCacheUpdateReady);
if (window.applicationCache.status === window.applicationCache.UPDATEREADY) {
appCacheUpdateReady();
}
// TODO: deal with jsUrls function which is moved to the router
$(document).ready(function () {
// Add anchor click handlers for internal links

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@ Terms of Use
============
------------
This site is _Copyright 2016 © - Collin Doering_ and is distributed under the following terms.
This site is _Copyright 2016-2021 © - Collin Doering_ and is distributed under the following terms.
1. All rights reserved on the "#! λ Slang" name, as well as on the
[banner image](/images/logo-banner.svg), favicons([1](/images/favicon.ico),

24
site
View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
function run_site_only() {
[ "$SITE_ONLY" == "true" ]
@ -21,24 +21,9 @@ case "$1" in
esac
case "$1" in
test)
if [[ "$2" == "-s" || "$2" == "--run-selenium" ]]; then
if ! type selenium &> /dev/null; then
echo "Failed to run Selenium. It must not be installed or not accessible on \$PATH!"
exit 1
fi
echo "Running Selenium..."
selenium 2> /dev/null &
sleep 3s
fi
# Test site
stack test
;;
gencss)
shift
stack exec gencss -- "$@"
gencss -- "$@"
;;
# Override of hakyll site commands
-h|--help)
@ -53,7 +38,6 @@ Available commands:
clean*
deploy*
gencss
test
Hakyll site commands:
build
@ -77,7 +61,7 @@ EOF
run_override_only && exit
# Only run hakyll site --help command if -- site only
run_site_only && stack exec blog-rekahsoft-ca -- --help | sed 's/\(Usage: \)blog-rekahsoft-ca/\1.\/site -/g'
run_site_only && blog-rekahsoft-ca -- --help | sed 's/\(Usage: \)blog-rekahsoft-ca/\1.\/site -/g'
;;
build)
run_override_only || ! run_site_only && stack build
@ -104,6 +88,6 @@ EOF
popd > /dev/null
;;&
*)
stack exec blog-rekahsoft-ca -- "$@"
blog-rekahsoft-ca -- "$@"
;;
esac

View File

@ -24,15 +24,10 @@
import Hakyll
import Control.Monad
import Data.Monoid (mconcat,(<>))
import Data.List (sortBy)
import Data.Map (toList)
import Data.Ord (comparing)
import System.Random
import System.FilePath (takeBaseName)
import System.Process
import System.Exit
import System.IO (hGetContents)
import Text.Parsec
import Text.Pandoc.Options
@ -96,205 +91,162 @@ myConfig = defaultConfiguration
}
main :: IO ()
main = do
-- Get a random number generator before going into Rules monad
stdGen <- getStdGen
main = hakyllWith myConfig $ do
match ("action/**" .||. "files/**" .||. "images/**" .||. "fonts/**" .||. "robots.txt") $ do
route idRoute
compile copyFileCompiler
hakyllWith myConfig $ do
match ("action/**" .||. "files/**" .||. "images/**" .||. "fonts/**" .||. "robots.txt") $ do
route idRoute
compile copyFileCompiler
match "css/**" $ do
route idRoute
compile compressCssCompiler
match "css/**" $ do
route idRoute
compile compressCssCompiler
match "lib/Skeleton/*.css" $ do
route $ gsubRoute "Skeleton" (const "css")
compile compressCssCompiler
match "lib/Skeleton/*.css" $ do
route $ gsubRoute "Skeleton" (const "css")
compile compressCssCompiler
match "templates/**" $ compile $ getResourceBody >>= saveSnapshot "original"
>> templateCompiler
match "templates/**" $ compile $ getResourceBody >>= saveSnapshot "original"
>> templateCompiler
-- Generate tags
tags <- buildTags ("posts/**" .&&. hasNoVersion) (fromCapture "tags/*1.html")
-- Generate tags
tags <- buildTags ("posts/**" .&&. hasNoVersion) (fromCapture "tags/*1.html")
-- Generate paginate
paginatedPosts <- buildPaginateWith
(fmap (paginateEvery numPaginatePages) . sortRecentFirst)
("posts/**" .&&. hasNoVersion)
(\n -> fromCapture "blog*.html" (show n))
-- Generate paginate
paginatedPosts <- buildPaginateWith
(fmap (paginateEvery numPaginatePages) . sortRecentFirst)
("posts/**" .&&. hasNoVersion)
(\n -> fromCapture "blog*.html" (show n))
clayDeps <- makePatternDependency $ fromGlob "clay/**.hs"
pageIds <- getMatches ("pages/**" .&&. complement "pages/blog.markdown")
fontIds <- getMatches "fonts/**"
imageIds <- getMatches "images/**"
cssIds <- getMatches "css/**"
jsIds <- getMatches "js/**"
libIds <- getMatches "lib/**"
rulesExtraDependencies [clayDeps] $ create ["default.css"] $ do
route idRoute
compile $ makeItem =<< (unsafeCompiler $ readProcess "gencss" ["compact"] "")
clayIds <- getMatches "clay/**.hs"
let manifestIds = clayIds ++ fontIds ++ imageIds ++ pageIds ++ cssIds ++ libIds ++ jsIds
match "css/**" $ do
route idRoute
compile compressCssCompiler
clayDeps <- makePatternDependency $ fromList clayIds
manifestDeps <- makePatternDependency $ fromList manifestIds
match "lib/Skeleton/*.css" $ do
route $ gsubRoute "Skeleton" (const "css")
compile compressCssCompiler
rulesExtraDependencies [clayDeps] $ create ["default.css"] $ do
route idRoute
compile $ makeItem =<< (unsafeCompiler $ do
(_, hout, _, ph) <- createProcess $ shell "stack build blog-rekahsoft-ca:gencss"
exitCode <- waitForProcess ph
if exitCode == ExitSuccess
then readProcess "stack" ["exec", "gencss", "--", "compact"] ""
else case hout of
Nothing -> fail "Error running 'stack build blog-rekahsoft-ca:gencss'"
Just hout' -> hGetContents hout' >>= fail)
match "templates/**" $ compile $ getResourceBody >>= saveSnapshot "original"
>> templateCompiler
rulesExtraDependencies [manifestDeps] $ create ["manifest.appcache"] $ do
route idRoute
compile $ do
manifestCacheRoutesMaybe <- sequence $ liftM getRoute (fontIds ++ pageIds ++ imageIds ++ cssIds ++ libIds ++ jsIds)
let randomNum = random stdGen :: (Int, StdGen)
randomStr = show . abs . fst $ randomNum
manifestStart = [ "CACHE MANIFEST"
, "# " ++ randomStr ]
manifestCacheSingles = [ "/default.css" ]
paginatedPostsCache = take 2 $ map (\(n,_) -> "/blog" ++ (show n) ++ ".html") $ toList $ paginateMap paginatedPosts
tagsCache = concatMap (\(t,ids) -> take 2 $ ["/tags/" ++ t ++ show n ++ ".html" | n <- [1..length $ paginateEvery numPaginatePages ids]]) $ tagsMap tags
manifestCacheFromIds = filter (not . null) $ fmap (maybe "" ("/"++)) manifestCacheRoutesMaybe
manifestCache = manifestCacheFromIds ++ tagsCache ++ paginatedPostsCache
manifestNetwork = [ "NETWORK:"
, "*"
, "http://*"
, "https://*" ]
makeItem . unlines $ manifestStart ++ [""] ++
manifestCacheSingles ++ manifestCache ++ [""] ++
manifestNetwork ++ [""]
-- Generate tag pages
forM_ (tagsMap tags) $ \(tag, identifiers) -> do
paginatedTaggedPosts <- buildPaginateWith
(fmap (paginateEvery numPaginatePages) . sortRecentFirst)
(fromList identifiers)
(\n -> fromCapture (fromGlob $ "tags/" ++ tag ++ "*.html") (show n))
match "css/**" $ do
route idRoute
compile compressCssCompiler
match "lib/Skeleton/*.css" $ do
route $ gsubRoute "Skeleton" (const "css")
compile compressCssCompiler
match "templates/**" $ compile $ getResourceBody >>= saveSnapshot "original"
>> templateCompiler
-- Generate tag pages
forM_ (tagsMap tags) $ \(tag, identifiers) -> do
paginatedTaggedPosts <- buildPaginateWith
(fmap (paginateEvery numPaginatePages) . sortRecentFirst)
(fromList identifiers)
(\n -> fromCapture (fromGlob $ "tags/" ++ tag ++ "*.html") (show n))
paginateRules paginatedTaggedPosts $ \pageNum pattern -> do
route $ gsubRoute " " (const "-") `composeRoutes` setExtension "html"
compile $ do
posts <- recentFirst =<< loadAllSnapshots pattern "content"
navCtx <- genNavContext "pages/blog.markdown"
let ctx = taggedPostCtx tags <>
paginateContext paginatedTaggedPosts pageNum <>
constField "tag" tag <>
listField "posts" (taggedPostCtx tags) (return posts)
indexCtx = if pageNum <= 2
then appCacheCtx <> navCtx
else navCtx
makeItem ""
>>= loadAndApplyTemplate "templates/tag-page.html" ctx
>>= loadAndApplyTemplate "templates/default.html" indexCtx
rulesExtraDependencies [tagsDependency tags] $ do
create [fromFilePath $ "tags/" ++ tag ++ ".xml"] $ do
route $ gsubRoute " " (const "-") `composeRoutes` setExtension "xml"
compile $ loadAllSnapshots (fromList identifiers) "content"
>>= fmap (take 10) . recentFirst
>>= renderAtom (feedConfiguration $ Just tag) (bodyField "description" <> defaultContext)
let pageRoute = gsubRoute "pages/" (const "") `composeRoutes` setExtension "html"
match ("pages/*" .&&. complement "pages/blog.markdown") $ version "nav-gen" $ do
route $ pageRoute
compile $ pandocCompiler
match "pages/blog.markdown" $ version "nav-gen" $ do
route $ constRoute "blog1.html"
compile $ pandocCompiler
paginateRules paginatedPosts $ \pageNum pattern -> do
route idRoute
paginateRules paginatedTaggedPosts $ \pageNum pattern -> do
route $ gsubRoute " " (const "-") `composeRoutes` setExtension "html"
compile $ do
posts <- recentFirst =<< loadAllSnapshots pattern "content"
navCtx <- genNavContext "pages/blog.markdown"
let ctx = taggedPostCtx tags <>
paginateContext paginatedPosts pageNum <>
paginateContext paginatedTaggedPosts pageNum <>
constField "tag" tag <>
listField "posts" (taggedPostCtx tags) (return posts)
indexCtx = if pageNum <= 2
then appCacheCtx <> navCtx
else navCtx
makeItem ""
>>= loadAndApplyTemplate "templates/pages/blog.html" ctx
>>= loadAndApplyTemplate "templates/tag-page.html" ctx
>>= loadAndApplyTemplate "templates/default.html" indexCtx
match ("pages/*" .&&. complement "pages/blog.markdown") $ do
route $ pageRoute
compile $ do
posts <- recentFirst =<< loadAllSnapshots "posts/**" "content"
rulesExtraDependencies [tagsDependency tags] $ do
create [fromFilePath $ "tags/" ++ tag ++ ".xml"] $ do
route $ gsubRoute " " (const "-") `composeRoutes` setExtension "xml"
compile $ loadAllSnapshots (fromList identifiers) "content"
>>= fmap (take 10) . recentFirst
>>= renderAtom (feedConfiguration $ Just tag) (bodyField "description" <> defaultContext)
-- Get the current Identifier
curId <- getUnderlying
let pageRoute = gsubRoute "pages/" (const "") `composeRoutes` setExtension "html"
let pageFilePath = toFilePath curId
pageName = takeBaseName pageFilePath
recentPosts = take 5 posts
pageTemplate = "templates/pages/" ++ pageName ++ ".html"
match ("pages/*" .&&. complement "pages/blog.markdown") $ version "nav-gen" $ do
route $ pageRoute
compile $ pandocCompiler
-- Generate navigation context
navCtx <- genNavContext pageFilePath
match "pages/blog.markdown" $ version "nav-gen" $ do
route $ constRoute "blog1.html"
compile $ pandocCompiler
let masterCtx =
listField "recentPosts" (taggedPostCtx tags) (return recentPosts) <>
listField "posts" (taggedPostCtx tags) (return posts) <>
tagCloudField "tagCloud" 65 135 tags <>
defaultContext
indexCtx = navCtx <> appCacheCtx
paginateRules paginatedPosts $ \pageNum pattern -> do
route idRoute
compile $ do
posts <- recentFirst =<< loadAllSnapshots pattern "content"
sectionCtx <- getResourceBody >>= genSectionContext
pg <- loadSnapshot (fromFilePath pageTemplate) "original"
>>= applyAsTemplate (sectionCtx <> masterCtx)
navCtx <- genNavContext "pages/blog.markdown"
(makeItem . itemBody) pg
>>= loadAndApplyTemplate "templates/default.html" indexCtx
let ctx = taggedPostCtx tags <>
paginateContext paginatedPosts pageNum <>
listField "posts" (taggedPostCtx tags) (return posts)
indexCtx = if pageNum <= 2
then appCacheCtx <> navCtx
else navCtx
match "posts/**" $ do
route $ setExtension "html"
compile $ do
indexCtx <- genNavContext "pages/blog.markdown"
makeItem ""
>>= loadAndApplyTemplate "templates/pages/blog.html" ctx
>>= loadAndApplyTemplate "templates/default.html" indexCtx
pandocCompilerWith pandocReaderOptions pandocWriterOptions
>>= saveSnapshot "content"
>>= loadAndApplyTemplate "templates/partials/post.html" (taggedPostCtx tags)
>>= loadAndApplyTemplate "templates/default.html" indexCtx
match ("pages/*" .&&. complement "pages/blog.markdown") $ do
route $ pageRoute
compile $ do
posts <- recentFirst =<< loadAllSnapshots "posts/**" "content"
create ["atom.xml"] $ do
route idRoute
compile $ do
let feedCtx = postCtx <> bodyField "description"
blogPosts <- loadAllSnapshots ("posts/**" .&&. hasNoVersion) "content"
>>= fmap (take 10) . recentFirst
renderAtom (feedConfiguration Nothing) feedCtx blogPosts
-- Get the current Identifier
curId <- getUnderlying
forM_ [("js/**", idRoute),
("lib/JQuery/*", gsubRoute "JQuery" $ const "js")] $ \(p, r) ->
match p $ do
route r
compile $ compressCssCompiler
let pageFilePath = toFilePath curId
pageName = takeBaseName pageFilePath
recentPosts = take 5 posts
pageTemplate = "templates/pages/" ++ pageName ++ ".html"
-- Generate navigation context
navCtx <- genNavContext pageFilePath
let masterCtx =
listField "recentPosts" (taggedPostCtx tags) (return recentPosts) <>
listField "posts" (taggedPostCtx tags) (return posts) <>
tagCloudField "tagCloud" 65 135 tags <>
defaultContext
indexCtx = navCtx <> appCacheCtx
sectionCtx <- getResourceBody >>= genSectionContext
pg <- loadSnapshot (fromFilePath pageTemplate) "original"
>>= applyAsTemplate (sectionCtx <> masterCtx)
(makeItem . itemBody) pg
>>= loadAndApplyTemplate "templates/default.html" indexCtx
match "posts/**" $ do
route $ setExtension "html"
compile $ do
indexCtx <- genNavContext "pages/blog.markdown"
pandocCompilerWith pandocReaderOptions pandocWriterOptions
>>= saveSnapshot "content"
>>= loadAndApplyTemplate "templates/partials/post.html" (taggedPostCtx tags)
>>= loadAndApplyTemplate "templates/default.html" indexCtx
create ["atom.xml"] $ do
route idRoute
compile $ do
let feedCtx = postCtx <> bodyField "description"
blogPosts <- loadAllSnapshots ("posts/**" .&&. hasNoVersion) "content"
>>= fmap (take 10) . recentFirst
renderAtom (feedConfiguration Nothing) feedCtx blogPosts
forM_ [("js/**", idRoute),
("lib/JQuery/*", gsubRoute "JQuery" $ const "js")] $ \(p, r) ->
match p $ do
route r
compile $ copyFileCompiler
---------------------------------------------------------------------------------------------------------
-- Functions & Constants --------------------------------------------------------------------------------

View File

@ -1,65 +0,0 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
# resolver: ghcjs-0.1.0_ghc-7.10.2
#
# The location of a snapshot can be provided as a file or url. Stack assumes
# a snapshot provided as a file might change, whereas a url resource does not.
#
# resolver: ./custom-snapshot.yaml
# resolver: https://example.com/snapshots/2018-01-01.yaml
resolver: lts-12.0
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# - location:
# git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
# subdirs:
# - auto-update
# - wai
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver
# using the same syntax as the packages field.
# (e.g., acme-missiles-0.3)
# extra-deps: []
# Override default flag values for local packages and extra-deps
# flags: {}
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=1.7"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

View File

@ -1,12 +0,0 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages: []
snapshots:
- completed:
size: 499178
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/12/0.yaml
sha256: 3e9a7b96708cd9196ce7e5396143725097a71f2e9ca8dc19f03f5082642bc1b5
original: lts-12.0

View File

@ -51,8 +51,8 @@
$partial("templates/partials/footer.html")$
<!-- External javascript libraries: JQuery, MathJax -->
<script src="/lib/js/jquery-1.11.2.js" type="text/javascript"></script>
<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"></script>
<script src="/lib/js/jquery-1.12.3.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML" type="text/javascript"></script>
<!-- Custom javascript for user interactivity -->
<script src="/js/default.js" type="text/javascript"></script>

View File

@ -2,7 +2,7 @@
<div class="container">
<footer>
<div class="six columns alpha" id="footer-left">
<p>&copy; Collin Doering 2016</p>
<p>&copy; Collin Doering 2016-2021</p>
</div>
<div class="six columns omega" id="footer-right">
<p>