Compare commits

...

77 Commits

Author SHA1 Message Date
Collin J. Doering 1e5ea8e220
drone-ci: Use guix-builder images instead of legacy guix image 2024-05-18 12:43:55 -04:00
Collin J. Doering 9adc412a3c
Supply gpg public key via wkd
* files/collin-doering.gpg: Removed file.
* templates/partials/business-card.html: Adjusted gpg public key link to point at wkd address.
2024-05-18 10:59:23 -04:00
Collin J. Doering d194575f06
infra/manifest.scm: Remove unused template provider 2023-12-26 21:07:18 -05:00
Collin J. Doering 2ebb239e8c
README.org: Add section 'Verifying a Release' 2023-12-26 21:06:50 -05:00
Collin J. Doering e4032ba6b3
site: Vendor MathJax from guix, removing dependency on CDN
* .gitignore: Ignore lib/MathJax symlink.
* README.org: Variety of changes, most notably using the README.org file as a literate source
file, which generates various development scripts and a Makefile. One such script is the
vendoring script, which is used to setup vendor symlinks.
* bootstrap.sh: Run emacs to tangle the literate README.org file, after which, run setup
vendored dependency links.
* guix.scm: Depend on js-mathjax, and vendor its built files into lib/MathJax before building
the site.
* pages/index.markdown: Remove stale reference to jquery-address.
* src/site.hs: Adjust hakyll rules to include MathJax from its now locally vendored location.
Additionally, do a tiny bit of rule tidying.
* templates/default.html: Remove stale reference to appcache. Remove reference to MathJax via
cdn and reference the now vendored variant.
2023-12-26 18:01:14 -05:00
Collin J. Doering bad2d07ce9
Improve development environment setup and documentation
* src/site.hs (siteRules): Refactor site building Rules into a new variable allowing for
reuse. Remove duplicate rules ()
(myConfig): Set deployCommand to something temporary.
(_devWatch): Entrypoint mean for use in development; it rebuilds the site builder, then
rebuilds the site.

* site (Removed file): Removed as it is no longer necessary. If additional cli functionality
is required, this can be done directly in haskell.

* guix.scm: Symlink 'site' now that it is no longer provided as a script.
(setenv): setenv only applies to the first (non-cached) run of `guix shell ...`,
because of this, remove setting PS1.
(native-inputs): Include development environment dependencies

* channels.scm (channel): Update rekahsoft-guix channel (noop, but originally updated while
looking at using overmind).

* bootstrap.sh: A script that generates various development scripts from the projects
README.org file.

* README.org: Cleanup and minor restructuring.
(Repository Structure): New (incomplete) section
(Prerequisites): New section
(Quick Start): New section
(Hakyll Site Commands): New section, with subheadings for various hakyll sub-commands.
(Makefile): New section; generates a makefile useful for development

* .gitignore: Ignore files generated by ./bootstrap.sh

* .ghci: ghci configuration file for hakyll development.

* .envrc: direnv configuration that loads a development environment for those who have
allowed it
2023-06-03 00:10:33 -04:00
Collin J. Doering fa89f9f158
Fix broken links (discovered via 'site check') 2023-05-28 22:04:47 -04:00
Collin J. Doering ce9d1fe54c
Update guix channels; make necessary adjustments (new ghc & clay)
* channels.scm (channel): Update all channels (guix and rekahsoft-guix)

* blog-rekahsoft-ca.cabal (cabal-version): Adjust base version now that ghc version 9.2.5 is in use; adjust clay to match ghc-clay package provided by updated guix channels.

* clay/*.hs: Adjust clay sources for api changes (swapping of function parameter order for
functions provided by the 'Clay.Border' module)
2023-05-20 22:32:21 -04:00
Collin J. Doering c51be05eb4
infra/main.tf: Correct typo in b9bee14 2023-05-04 23:00:14 -04:00
Collin J. Doering b9bee14f30
infra/main.tf: Adjust deployment script to account for unchange site file timestamps
* infra/main.tf: See 93dfdb0 for more detail regarding the deployment issue. In addition,
have the deployment script correctly fail if any command does not succeed.
2023-05-04 22:55:55 -04:00
Collin J. Doering b11aa217af
infra/main.tf: Correct typo in 93dfdb0 2023-05-02 22:01:23 -04:00
Collin J. Doering 93dfdb08fd
infra/main.tf: Use --size-only option until 'aws s3 sync' can use checksums
* infra/main.tf: This avoid a deployment issue after switching to guix for builds. Namely,
after to switching to guix to build this project, all built site files will have a datetime
of Unix epoch 0, which will then never be updated when running 'aws s3 sync ...' because the
files in the bucket were deployed before the site was built using guix, so they have newer
timestamps. Using the '--size-only' option improves this situation, in that files that are
modified and have a different size then the original will be updated, but files that are
changed but, by chance have the same size, will not be updated. Ironically, the update that I
discovered this on will end up resulting the same file sizes (changing the copyright date).
2023-05-02 19:14:36 -04:00
Collin J. Doering cee5d48466
guix.scm: Reuse %srcdir instead of calling getcwd 2023-04-30 21:16:37 -04:00
Collin J. Doering 143b38c89c
TODO.org: Remove TODOs 2023-04-30 17:21:00 -04:00
Collin J. Doering f16712cc38
Update copyright year in a variety of files 2023-04-30 13:24:05 -04:00
Collin J. Doering 54c48b55b8
infra: Remove unused template provider 2022-10-16 21:24:00 -04:00
Collin J. Doering d02d329bbc
TODO.org: Move Archive section to the top and add a new TODO item 2021-12-18 21:12:31 -05:00
Collin J. Doering 6b1129aa1a
TODO.org: Mark completed, but forgotten tasks and create Archive heading 2021-12-18 20:44:29 -05:00
Collin J. Doering 9171aa88ad
Replace broken piwiki analytics with self-hosted plausible
* templates/partials/analytics.html: Upon a client load of the website, determine which site
they are viewing (dev, staging, production) and setup plausible analytics appropriately.

* templates/default.html: Remove non-functional piwiki script reference and replace with a
reference to the new partial template for analytics

* src/site.hs (main): Remove remnant of application cache support

* js/default.js: Remove unneeded analytics code specific to piwiki. No additions were made here in support of plausible

* TODO.org: Mark associated TODO item DONE
2021-12-18 20:37:59 -05:00
Collin J. Doering 913c2f0e67
.drone/ci.libsonnet: Add and renamed methods relocated from .drone.jsonnet: guix.pipeline, guix.step, awsDeployStep
* .drone.yml: Update file after updates to .drone.jsonnet

* .drone.jsonnet: Relocate guix_pipeline, guix_step, and deployStep to .drone/ci.libsonnet
library. Adjust existing function names to match new names in library.

Explicitly pass PLAN environment variable to ci.awsDeployStep for plan and apply stages
2021-12-11 13:19:55 -05:00
Collin J. Doering eaf41637ab
.gitignore: Do not ignore hidden files 2021-12-11 12:53:30 -05:00
Collin J. Doering 284abdf916
.drone.jsonnet: Move ci local to its own library in .drone/ci.libsonnet 2021-12-11 12:53:01 -05:00
Collin J. Doering faad56813b
.drone.jsonnet: Remove setting of root user in /etc/passwd
Adding the root user to /etc/passwd was only required when attempting to run guix shell
`-C|--container' commands, however guix shell containers will not work without privileged
docker, so when running via ci, we depend on the provided minimal guix container (build with
guix via 'guix pack -f docker ...').
2021-12-10 22:32:42 -05:00
Collin J. Doering 572f00a79a
README.org: Set drone ci badge to use the master branch 2021-12-09 23:18:41 -05:00
Collin J. Doering 4a1bbf41bf
.drone.jsonnet: Remove unnecessary comment 2021-12-09 23:18:20 -05:00
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
38 changed files with 4691 additions and 3539 deletions

21
.drone.jsonnet Normal file
View File

@ -0,0 +1,21 @@
local ci = import '.drone/ci.libsonnet';
[
ci.guix.pipeline("validate").withTrigger(ci.trigger.new().withEvent(["push", "pull_request", "tag"])).withSteps([
ci.guix.stepTimeMachine("build", "build -f guix.scm"),
ci.promoteStep("staging"),
ci.promoteStep("production"),
]),
ci.guix.pipeline("deploy").withTrigger(ci.trigger.new().withEvent("promote")).withSteps([
ci.awsDeployStep("init", "setup"),
ci.awsDeployStep("plan").withEnv({
PLAN: "out.plan"
}).withRuntimeEnvVar({
TF_VAR_site_static_files_dir: "$(guix time-machine -C channels.scm -- build -f guix.scm | grep -e '^.*-site$')"
}),
ci.awsDeployStep("deploy").withEnv({
PLAN: "out.plan"
}),
])
]

View File

@ -1,12 +1,133 @@
---
kind: pipeline
type: docker
name: blog-rekahsoft-ca
workspace:
path: /drone/blog-rekahsoft-ca
name: validate
node:
guix: "on"
steps:
- name: build-site
image: docker.nexus.home.rekahsoft.ca/fpco/stack-build:lts-12.0
commands:
- ./site build
- commands:
- guix time-machine -C channels.scm -- build -f guix.scm
image: docker.nexus.home.rekahsoft.ca/guix-builder:latest
name: build
pull: if-not-exists
- 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
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
environment:
DRONE_TOKEN:
from_secret: drone_token
image: docker.nexus.home.rekahsoft.ca/drone/cli:1.4-alpine
name: promote-staging
pull: if-not-exists
when:
branch:
- master
event:
- push
- 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
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
environment:
DRONE_TOKEN:
from_secret: drone_token
image: docker.nexus.home.rekahsoft.ca/drone/cli:1.4-alpine
name: promote-production
pull: if-not-exists
when:
branch:
- master
event:
- push
trigger:
event:
- push
- pull_request
- tag
type: docker
---
kind: pipeline
name: deploy
node:
guix: "on"
steps:
- commands:
- cd infra
- '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
image: docker.nexus.home.rekahsoft.ca/guix-builder:latest
name: init
pull: if-not-exists
- commands:
- export TF_VAR_site_static_files_dir="$(guix time-machine -C channels.scm -- build
-f guix.scm | grep -e '^.*-site$')"
- cd infra
- '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
image: docker.nexus.home.rekahsoft.ca/guix-builder:latest
name: plan
pull: if-not-exists
- commands:
- cd infra
- '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
image: docker.nexus.home.rekahsoft.ca/guix-builder:latest
name: deploy
pull: if-not-exists
trigger:
event:
- promote
type: docker

195
.drone/ci.libsonnet Normal file
View File

@ -0,0 +1,195 @@
{
local ci = self,
local droneStatus = ['success', 'failure'],
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)
},
guix:: {
pipeline(name)::
ci.pipeline.new()
.withName(name)
.withType("docker")
.withNode({ "guix": "on"}),
step(name, commands, image="docker.nexus.home.rekahsoft.ca/guix-builder:latest")::
ci.pipeline.step.new(name, image).withPullIfNotExists().withCommands(commands),
stepTimeMachine(name, commands, cwd=".", channels="channels.scm", image="docker.nexus.home.rekahsoft.ca/guix-builder:latest")::
ci.pipeline.step.new(name, image).withPullIfNotExists().withCommands(
// Conditionally change directory
(if cwd == "."
then [] else [std.format("cd %s", cwd)]) +
// 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])),
},
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"
})),
awsDeployStep(name, target=name, args=[])::
ci.guix.stepTimeMachine(
name,
std.format('shell -m manifest.scm -- make %s ENV="${DRONE_DEPLOY_TO}" %s', [target, std.join(" ", args)]),
cwd="infra",
channels="../channels.scm")
.withEnv(ci.env_from_secret({
AWS_ACCESS_KEY_ID: "aws_access_key_id",
AWS_SECRET_ACCESS_KEY: "aws_secret_access_key",
})),
}

10
.envrc Normal file
View File

@ -0,0 +1,10 @@
use_guix-shell() {
CHANNEL_FILE=channels.scm
if [ -f $CHANNEL_FILE ]; then
eval "$(guix time-machine -C $CHANNEL_FILE -- shell "$@" --search-paths)"
else
eval "$(guix shell "$@" --search-paths)"
fi
}
use guix-shell -f guix.scm -Df guix.scm

3
.ghci Normal file
View File

@ -0,0 +1,3 @@
:set -fwarn-unused-binds -fwarn-unused-imports
:set -isrc
:load src/site.hs

17
.gitignore vendored
View File

@ -1,5 +1,4 @@
# Editor specific files
.*
*~
.dir-locals.el
.tern-port
@ -7,7 +6,6 @@
# Haskell
*.o
*.hi
.stack
# Hakyll
_site
@ -15,7 +13,14 @@ _cache
dist
# Terraform
.terraform
terraform.tfstate.d
*.local.tfvars
*.plan
infra/.terraform
infra/terraform.tfstate.d
infra/*.local.tfvars
infra/*.plan
# Vendored libraries
lib/MathJax
# Generated by ./bootstrap
Makefile
scripts

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-2023 © - 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/

390
README.org Normal file
View File

@ -0,0 +1,390 @@
#+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?ref=refs/heads/master" 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]] is 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.
* 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
[[https://guix.gnu.org/][Gnu Guix]] is used to package this project and manage its dependencies, as well as to provide
reproducible development environments.
** Prerequisites
The only prerequisite for starting a development environment for this project is [[https://guix.gnu.org/][GNU Guix]].
Optionally, [[https://direnv.net/][direnv]] can be used to enable a non-containerized development environment that is
abridged with your existing shell.
** Quick Start
First run the bootstrap script, which uses this documentation to generate a ~Makefile~ that
can be used for development.
#+name: bootstrap
#+begin_src sh
./bootstrap.sh
#+end_src
Then run the development 'auto-watching' environment:
#+begin_src sh
make
#+end_src
This starts a containerized local development environment that uses [[https://github.com/ndmitchell/ghcid/][ghcid]] to watch haskell
sources and restart hakyll's [[*Watch][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][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 :mkdirp yes :tangle ./scripts/start-development-environment.sh :tangle-mode (identity #o555)
guix time-machine -C channels.scm -- shell -CN -E '^LANG$' -E '^TERM$' -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. 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
[[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 environment, run the following:
#+begin_src sh :mkdirp yes :tangle ./scripts/start-deployment-environment.sh :tangle-mode (identity #o555)
cd infra
guix time-machine -C channels.scm -- shell -CN -E '^LANG$' -E '^TERM$' -E '^AWS.*$'
#+end_src
*** Composing Site Development and Deployment Environments
#+begin_src sh :mkdirp yes :tangle ./scripts/start-development-and-deployment-environment.sh :tangle-mode (identity #o555)
guix time-machine -C channels.scm -- shell -CN -E '^LANG$' -E '^TERM$' -E '^AWS.*$' -f guix.scm -Df guix.scm -m infra/manifest.scm $@
#+end_src
** 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:
#+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
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.
#+begin_src sh
site watch
#+end_src
*** TODO ~site deploy~ command
#+begin_src sh
site deploy
#+end_src
** Clean up Guix Store
#+begin_src sh :mkdirp yes :tangle ./scripts/clean-guix-store.sh :tangle-mode (identity #o555)
guix gc --list-dead | grep -e '^/gnu/store/.*-blog-rekahsoft-ca-.*' | xargs guix gc -D
#+end_src
** 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, [[https://github.com/ndmitchell/ghcid/][ghcid]] can be used for this, and is included in the projects development
dependencies, specified in the ~guix.scm~ file.
#+name: watch-all
#+begin_src sh :mkdirp yes :tangle ./scripts/watch-all.sh :tangle-mode (identity #o555)
ghcid --test _devWatch
#+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 :mkdirp yes :tangle ./scripts/build-release.sh :tangle-mode (identity #o555)
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 (also available as ~site~), and ~gencss~ 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.
#+begin_src sh
guix shell python-wrapper -- python -m http.server -d $(guix time-machine -C channels.scm -- build -f guix.scm | grep -E '^.*-site') 3000
#+end_src
** 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][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
#+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.
* TODO Writing a Blog Post
The most natural way to edit and preview a post is to use [[https://direnv.net/][direnv]] along with this repository,
which uses ~guix shell~ to transparently provide all necessary tools, including [[*Hakyll Site Commands][Hakyll Site
Commands]]. When using direnv, a containerized environment will not be used, however for
content development, this is not a concern.
#+begin_src sh
guix time-machine -C channels.scm -- shell -CN -E LANG -E TERM -f guix.scm
#+end_src
* DOING Vendor external libraries using Guix
Some ...
#+begin_src sh :mkdirp yes :tangle ./scripts/vendor-deps.sh :tangle-mode (identity #o555)
[ -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
#+end_src
* Makefile
In order to simplify running the various commands outlined throughout this document, a
~Makefile~ is defined below.
#+begin_src makefile :noweb yes :tangle Makefile :tangle-mode (identity #o444)
# 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
#+end_src
* Continuous Integration & Delivery
** TODO Generate ~.drone.yaml~
#+begin_src sh
drone jsonnet --stream --format
#+end_src
*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.
#+begin_src shell
docker run -v ${PWD}:/tmp/app -w /tmp/app --rm -it drone/cli:<versin> jsonnet --stream --format
#+end_src
* Known Issues
If you have an issue while browsing [[http://www.blog.rekahsoft.ca][my blog]] please let me know via [[https://www.blog.rekahsoft.ca/contact.html][email]].

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.16 && <4.17,
hakyll >= 4.15 && <4.16,
pandoc >= 1.13,
parsec >= 3.1,
filepath >= 1.3,
@ -89,8 +89,8 @@ executable gencss
other-extensions: OverloadedStrings
-- Other library packages from which modules are imported.
build-depends: base >=4.11 && <4.12,
clay >=0.13 && <0.14,
build-depends: base >=4.16 && <4.17,
clay >=0.14 && <0.15,
text >=1.2 && <1.3
-- Directories containing source files.

5
bootstrap.sh Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
guix time-machine -C channels.scm -- shell -C emacs git -- emacs -q README.org --batch --eval '(org-babel-tangle)'
./scripts/vendor-deps.sh

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
"a4e9842a70775a54bbe1369881b739e7ea9a6432")
(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
"e016a7e7a9eb3d27a4d6368861222e9916e27a47")
(introduction
(make-channel-introduction
"191cdaa0947657e0c85fe89ebbb8e7b1e7a8e0a4"
(openpgp-fingerprint
"F8D5 46F3 AF37 EF53 D1B6 48BE 7B4D EB93 212B 3022")))))

View File

@ -55,7 +55,7 @@ navigation = do
(backgroundPosition $ positioned (px 304) nil)
"#nav" ? do
border solid (px 2) black
border (px 2) solid black
borderRightWidth 0
borderLeftWidth 0
backgroundImage $ url "/images/diagonal-stripes.png"
@ -126,8 +126,8 @@ statusMessage :: Css
statusMessage = do
"#status" ? do
display none
border solid (px 1) black
borderTop solid nil black
border (px 1) solid black
borderTop nil solid black
borderBottomRightRadius (px 5) (px 5)
borderBottomLeftRadius (px 5) (px 5)
backgroundColor $ rgb 146 208 240

View File

@ -50,7 +50,7 @@ aPost = do
header ? do
marginBottom (em 0.8)
border solid (px 2) "#eee"
border (px 2) solid "#eee"
sym borderRadius (px 3)
sym padding (em 0.35)
paddingLeft (px 65)
@ -84,7 +84,7 @@ aPost = do
footer ? do
padding (em 0.75) nil (em 0.25) nil
borderTop solid (px 1) "#eee"
borderTop (px 1) solid "#eee"
".read-more" ? fontWeight bold
".no-teaser" ? do
@ -122,7 +122,7 @@ aPost = do
businessCard :: Css
businessCard = do
"#business-card" ? do
border solid (px 2) black
border (px 2) solid black
sym borderRadius (px 5)
sym padding (px 10)
minHeight (px 215)
@ -133,7 +133,7 @@ businessCard = do
backgroundImage $ url "/images/business-card.png"
backgroundSize cover
backgroundPosition $ placed sideCenter sideCenter
border solid (px 1) black
border (px 1) solid black
sym borderRadius (px 10)
minHeight (px 215)
minWidth (px 150)
@ -141,7 +141,7 @@ businessCard = do
marginRight (px 10)
".info" ? do
borderTop solid (px 2) black
borderTop (px 2) solid black
overflow hidden
paddingTop (px 8)
@ -185,11 +185,11 @@ srcCodeBlock = do
<> table # ".sourceCode" ** pre ? do
sym margin nil
sym padding nil
border none nil black
border nil none black
verticalAlign vAlignBaseline
td # ".lineNumbers" ? do
borderRight solid (px 1) "#AAAAAA"
borderRight (px 1) solid "#AAAAAA"
textAlign $ alignSide sideRight
color "#AAAAAA"
paddingLeft (px 8)
@ -222,7 +222,7 @@ srcCodeBlock = do
postFigures :: Css
postFigures = do
figure ? do
border solid (px 1) black
border (px 1) solid black
sym borderRadius (px 3)
clear both
@ -230,7 +230,7 @@ postFigures = do
img <? do
display block
width (pct 100)
borderBottom solid (px 1) black
borderBottom (px 1) solid black
cursor pointer
figcaption # ":before" <? do
@ -249,7 +249,7 @@ inlinePostImages = article # ".post" ? do
clear clearRight
float floatRight
width (pct 30)
border solid (px 1) black
border (px 1) solid black
sym borderRadius (px 3)
sym margin (em 1)
cursor pointer

View File

@ -32,7 +32,7 @@ makeBorderBox pad backCol =
backCol' = fromMaybe (rgba 250 250 255 165) backCol
in do
backgroundColor backCol'
border solid (px 1) "#888"
border (px 1) solid "#888"
borderRadius (px 5) (px 5) (px 5) (px 5)
-- boxShadow (px 2) (px 5) (px 2) "#888"
sym padding pad'

View File

@ -1,950 +0,0 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=PilA
-----END PGP PUBLIC KEY BLOCK-----

85
guix.scm Normal file
View File

@ -0,0 +1,85 @@
;; (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)
(gnu packages javascript)
(gnu packages haskell-apps)
(rekahsoft-gnu packages haskell-web)
(git))
(define %srcdir
(dirname (current-filename)))
(define %blog-rekahsoft-ca
(let ((commit (oid->string
(reference-target
(repository-head (repository-open %srcdir)))))
(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)
("make" ,gnu-make)
("ghcid" ,ghcid)))
(inputs `(("ghc-hakyll" ,ghc-hakyll)
("ghc-clay" ,ghc-clay)
("js-mathjax" ,js-mathjax)))
(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")))
(symlink (string-append out "/bin/blog-rekahsoft-ca") (string-append out "/bin/site"))
#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")))
;; Copy (vendor) dependencies: MathJax
(copy-recursively (string-append (assoc-ref %build-inputs "js-mathjax")
"/share/javascript/mathjax")
"lib/MathJax" #:follow-symlinks? #t)
;; 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,15 +33,11 @@ provider "null" {
}
provider "random" {
version = "~> 2.1"
}
provider "template" {
version = "~> 2.1"
version = "= 2.1.2"
}
#
# Local values to be re-used throughout this template
# Local values to be re-used throughout
locals {
common_tags = {
@ -55,18 +51,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 +112,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 +221,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 +257,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 +343,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 {
@ -394,6 +389,8 @@ resource "null_resource" "deploy_app" {
provisioner "local-exec" {
interpreter = ["bash", "-c"]
command = <<SCRIPT
set -eo pipefail;
: Create temporary aws config and credentials files
export AWS_CONFIG_FILE=$(mktemp);
export AWS_SHARED_CREDENTIALS_FILE=$(mktemp);
@ -403,11 +400,20 @@ aws configure --profile ${aws_iam_user.app_deploy.name} set aws_access_key_id ${
aws configure --profile ${aws_iam_user.app_deploy.name} set aws_secret_access_key ${aws_iam_access_key.app_deploy.secret};
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}/;
: Create a random string to be used as a temporary directory name;
TMPDIR=/tmp/$(printf '%s' {a..z} {A..Z} {0..9} | fold -w1 | shuf | paste -s -d '' | head -c16);
: Cleanup temporary aws config and credentials files
rm $${AWS_CONFIG_FILE} $${AWS_SHARED_CREDENTIALS_FILE};
: Copy site files so that they get a new date/time stamp, allowing 's3 sync' to operate correctly;
cp -r ${var.site_static_files_dir} $${TMPDIR};
: Allow copied site files to be removable after deployment;
chmod u+rw -R $${TMPDIR};
: Sync latest app build to s3 bucket;
aws --profile ${aws_iam_user.app_deploy.name} s3 sync --delete $${TMPDIR} s3://${aws_s3_bucket.static.id}/;
: Cleanup temporary aws config, its credentials files as well as the copied site files;
rm -r $${AWS_CONFIG_FILE} $${AWS_SHARED_CREDENTIALS_FILE} $${TMPDIR};
SCRIPT
}

36
infra/manifest.scm Normal file
View File

@ -0,0 +1,36 @@
;; (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"
"awscliv2"
"nss-certs"))

View File

@ -1,33 +0,0 @@
{
"Version": "2008-10-17",
"Id": "StaticBucketPolicy",
"Statement": [
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"AWS": "${cloudfront_arn}"
},
"Action": "s3:GetObject",
"Resource": "${bucket_arn}/*"
},
{
"Sid": "2",
"Effect": "Allow",
"Principal": {
"AWS": "${user_arn}"
},
"Action": ["s3:ListBucket"],
"Resource": "${bucket_arn}"
},
{
"Sid": "3",
"Effect": "Allow",
"Principal": {
"AWS": "${user_arn}"
},
"Action": "s3:*",
"Resource": "${bucket_arn}/*"
}
]
}

View File

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

View File

@ -27,9 +27,6 @@
/*global jQuery, MathJax*/
//------------------------
// Global array for processing piwik analytics commands
var _paq = _paq || [];
(function ($, mj) {
"use strict";
@ -196,49 +193,6 @@ var _paq = _paq || [];
return spec;
}()),
analytics = (function () {
var inited = false,
spec = {
trackPageView: trackPageView,
debugEnable: function () {
init();
}
};
function trackPageView (href) {
if (inited) {
_paq.push(["setDocumentTitle", document.domain + href]);
_paq.push(["trackPageView"]);
}
}
function init() {
if (!inited) {
_paq.push(["setDoNotTrack", true]);
_paq.push(["enableLinkTracking"]);
_paq.push(["setTrackerUrl", "//analytics.rekahsoft.ca/piwik.php"]);
_paq.push(["setSiteId", 1]);
inited = true;
}
}
// Initialize piwik.js when site is initially loaded
router.onInit(function () {
if (document.domain != "localhost") {
init();
trackPageView('/');
}
});
// Track page views with piwik each time the url changes
router.onChange(function (url, dta) {
trackPageView(url);
});
return spec
}()),
site = (function () {
var status = (function () {
var messages = [],
@ -345,17 +299,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

@ -15,7 +15,6 @@ This website was proudly made with open source software! Specifically:
- [Clay][]
- [Skeleton][]
- [JQuery][]
- [JQuery-address][]
- [MathJax][]
- [Inkscape][]
- [Gimp][]
@ -28,7 +27,7 @@ Terms of Use
============
------------
This site is _Copyright 2016 © - Collin Doering_ and is distributed under the following terms.
This site is _Copyright 2016-2023 © - 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),

View File

@ -10,8 +10,8 @@ Currently as some may have noticed, the "See Comments" link under each individua
does nothing. This is because I have been struggling to find a good solution to handle
comments. Of course many people choose to use Disqus. Unfortunately due to the proprietary
nature of Disqus, I refuse to use it (See
[this](http://blog.irukado.org/2013/12/disqus-considered-harmful/)). There are some open source
solutions including:
[this](http://web.archive.org/web/20150323224820/http://blog.irukado.org/2013/12/disqus-considered-harmful/)).
There are some open source solutions including:
[Isso](http://posativ.org/isso/)
~ - Written in Python.
@ -25,14 +25,12 @@ There are quite a few others, though I haven't spent much time investigating the
them are incomplete or unmaintained. See <https://news.ycombinator.com/item?id=6818416> for a
decent discussion of "Disqus Alternatives".
I've tried Isso but because my blog is a Single Page Application (SPA) Isso doesn't function
correctly. Isso expects the current URL to be a direct link to the post html file that is being
commented on, but in the case of my blog it is a virtual url (Eg. this post,
<http://blog.rekahsoft.ca/#/posts/about-comments.html> doesn't work with Isso but
<http://blog.rekahsoft.ca/posts/about-comments.html> would but links directly to the html snippet
file instead of the entire post page.Forgive my vague explanation, its been a few months since
I last played with Isso. Its too bad I couldn't get it to function because from all the open
source options, it seems to be the most mature solution.
I've tried Isso but because my blog is a Single Page Application (SPA) where pages can be
addressed directly or using a anchor url. For example, this post can be reached via
[/#/posts/about-comments.html]() or [/posts/about-comments.html](). Forgive my vague
explanation, its been a few months since I last played with Isso. Its too bad I couldn't get
it to function because from all the open source options, it seems to be the most mature
solution.
Another option that came to mind is to use a sub-reddit for my blog and post new threads for
each blog post as a way for people to submit comments. I don't like this as the content

View File

@ -137,6 +137,6 @@ to obtain a *FPGA* and complete the implementation (will a VGA screen and a PS2
keyboard). Also just to reintegrate, for those of those interested in the inner workings of a
computer, I highly recommend checking out the *Nand to Tetris* course.
[hack-git]: http://git.rekahsoft.ca/hack/
[hack-docs]: http://git.rekahsoft.ca/hack/about
[hack-git]: https://git.rekahsoft.ca/rekahsoft/hack/
[hack-docs]: https://git.rekahsoft.ca/rekahsoft/hack/src/branch/master/README.md
[GtkWave]: http://gtkwave.sourceforge.net/

109
site
View File

@ -1,109 +0,0 @@
#!/bin/bash
function run_site_only() {
[ "$SITE_ONLY" == "true" ]
}
function run_override_only() {
[ "$OVERRIDE_ONLY" == "true" ]
}
case "$1" in
-)
OVERRIDE_ONLY=true
shift
;;
--)
SITE_ONLY=true
shift
;;
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 -- "$@"
;;
# Override of hakyll site commands
-h|--help)
run_override_only || ! run_site_only && cat << EOF
Wraps hakyll's provided site tool to augment certain commands.
Usage:
./site [-|--] COMMAND
Available commands:
build*
clean*
deploy*
gencss
test
Hakyll site commands:
build
check
clean
deploy
preview
rebuild
server
watch
Starred (*) commands indicate a overridden hakyll site command. However once the override is
run, the corresponding hakyll command is then run. This can be disabled with by specifying '--'
as the first argument, which will then pass all remaining arguments to the hakyll site command.
Similarily, to only run the override, specify '-' as the first argument.
For more details about hakyll site commands and options, see './site -- --help'.
EOF
# Only run hakyll site --help command if override was not run
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'
;;
build)
run_override_only || ! run_site_only && stack build
run_override_only && exit $?
;;&
clean)
run_override_only || ! run_site_only && stack clean
run_override_only && exit $?
;;&
deploy)
pushd infra > /dev/null
# Only run hakyll site deploy command when site-only is given. Additionally, when
# neither site-only or override-only are given, run only the override. The deploy
# override uses terraform which is also setup to deploy the hakyll site static files
run_override_only || ! run_site_only && (
export PLAN=".plans/local-$(date +%F_%R).plan"
[ ! -d .plans ] && mkdir .plans
make plan deploy
) && exit $?
run_site_only && export S3_BUCKET="$(terraform output s3_bucket_static)"
popd > /dev/null
;;&
*)
stack exec 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
@ -89,22 +84,29 @@ pandocWriterOptions = defaultHakyllWriterOptions
myConfig :: Configuration
myConfig = defaultConfiguration
{ deployCommand = "echo 'Deploying website...' && " ++
{ deployCommand = "echo 'TODO (what to do with this cmd) Deploying website...' && " ++
"aws s3 sync _site/ s3://$S3_BUCKET &&" ++
"echo 'Done!'"
, previewPort = 3000
}
main :: IO ()
main = do
-- Get a random number generator before going into Rules monad
stdGen <- getStdGen
hakyllWith myConfig $ do
match ("action/**" .||. "files/**" .||. "images/**" .||. "fonts/**" .||. "robots.txt") $ do
siteRules :: Rules ()
siteRules = do
match ("js/**"
.||. "files/**"
.||. "images/**"
.||. "fonts/**"
.||. "robots.txt") $ do
route idRoute
compile copyFileCompiler
forM_ [("lib/MathJax/fonts/HTML-CSS/**", gsubRoute "lib/MathJax/" $ const ""),
("lib/MathJax/**" .&&. complement "lib/MathJax/fonts", gsubRoute "lib/" $ const ""),
("lib/JQuery/*", gsubRoute "JQuery" $ const "js")] $ \(p, r) ->
match p $ do
route r
compile $ copyFileCompiler
match "css/**" $ do
route idRoute
compile compressCssCompiler
@ -125,61 +127,11 @@ main = do
("posts/**" .&&. hasNoVersion)
(\n -> fromCapture "blog*.html" (show n))
pageIds <- getMatches ("pages/**" .&&. complement "pages/blog.markdown")
fontIds <- getMatches "fonts/**"
imageIds <- getMatches "images/**"
cssIds <- getMatches "css/**"
jsIds <- getMatches "js/**"
libIds <- getMatches "lib/**"
clayIds <- getMatches "clay/**.hs"
let manifestIds = clayIds ++ fontIds ++ imageIds ++ pageIds ++ cssIds ++ libIds ++ jsIds
clayDeps <- makePatternDependency $ fromList clayIds
manifestDeps <- makePatternDependency $ fromList manifestIds
clayDeps <- makePatternDependency $ fromGlob "clay/*.hs"
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)
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 ++ [""]
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
compile $ makeItem =<< (unsafeCompiler $ readProcess "gencss" ["compact"] "")
-- Generate tag pages
forM_ (tagsMap tags) $ \(tag, identifiers) -> do
@ -199,9 +151,7 @@ main = do
paginateContext paginatedTaggedPosts pageNum <>
constField "tag" tag <>
listField "posts" (taggedPostCtx tags) (return posts)
indexCtx = if pageNum <= 2
then appCacheCtx <> navCtx
else navCtx
indexCtx = navCtx
makeItem ""
>>= loadAndApplyTemplate "templates/tag-page.html" ctx
@ -234,9 +184,7 @@ main = do
let ctx = taggedPostCtx tags <>
paginateContext paginatedPosts pageNum <>
listField "posts" (taggedPostCtx tags) (return posts)
indexCtx = if pageNum <= 2
then appCacheCtx <> navCtx
else navCtx
indexCtx = navCtx
makeItem ""
>>= loadAndApplyTemplate "templates/pages/blog.html" ctx
@ -263,7 +211,7 @@ main = do
listField "posts" (taggedPostCtx tags) (return posts) <>
tagCloudField "tagCloud" 65 135 tags <>
defaultContext
indexCtx = navCtx <> appCacheCtx
indexCtx = navCtx
sectionCtx <- getResourceBody >>= genSectionContext
pg <- loadSnapshot (fromFilePath pageTemplate) "original"
@ -290,11 +238,13 @@ main = do
>>= 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 $ compressCssCompiler
_devWatch :: IO ()
_devWatch = do
_ <- hakyllWithExitCodeAndArgs myConfig (Options { verbosity = False, optCommand = Rebuild }) $ siteRules
hakyllWithArgs myConfig (Options { verbosity = False, optCommand = Watch "localhost" 3000 False }) $ siteRules
main :: IO ()
main = hakyllWith myConfig siteRules
---------------------------------------------------------------------------------------------------------
-- Functions & Constants --------------------------------------------------------------------------------
@ -334,9 +284,6 @@ postCtx = dateField "date" "%B %e, %Y" <>
taggedPostCtx :: Tags -> Context String
taggedPostCtx tags = tagsField "tags" tags <> postCtx
appCacheCtx :: Context String
appCacheCtx = constField "appcache" "true"
pageWeight :: (Functor f, MonadMetadata f) => Item a -> f Int
pageWeight i = fmap (maybe 0 read) $ getMetadataField (itemIdentifier i) "weight"

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

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html class="en" $if(appcache)$manifest="/manifest.appcache"$endif$>
<html class="en">
<head>
<!-- Basic Page Needs -->
<meta charset="utf-8">
@ -24,9 +24,7 @@
</head>
<body>
<!-- Piwik -->
<script type="text/javascript" async defer src="//analytics.rekahsoft.ca/piwik.js"></script>
<noscript><img src="//analytics.rekahsoft.ca/piwik.php?idsite=1" style="border:0;display:none;" alt="" /></noscript>
$partial("templates/partials/analytics.html")$
$partial("templates/partials/logo-banner.html")$
$partial("templates/partials/nav.html")$
@ -51,8 +49,9 @@
$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="/MathJax/MathJax.js" type="text/javascript"></script>
<script src="/MathJax/config/TeX-MML-AM_CHTML.js" type="text/javascript"></script>
<!-- Custom javascript for user interactivity -->
<script src="/js/default.js" type="text/javascript"></script>

View File

@ -0,0 +1,21 @@
<script>
var ref = document.createElement("script");
ref.setAttribute("type", "text/javascript");
var script = "https://analytics.rekahsoft.ca/js/plausible.js";
switch (window.location.hostname) {
case "www.blog.rekahsoft.ca":
ref.setAttribute("data-domain", "blog.rekahsoft.ca");
break;
case "www.blog.staging.rekahsoft.ca":
ref.setAttribute("data-domain", "blog.staging.rekahsoft.ca");
break;
default:
script = "https://analytics.rekahsoft.ca/js/plausible.exclusions.local.js";
ref.setAttribute("data-domain", "localhost:3000");
ref.setAttribute("data-exclude", "localhost:3000");
}
ref.setAttribute("src", script);
document.getElementsByTagName("head")[0].appendChild(ref)
</script>

View File

@ -5,7 +5,7 @@
</p>
<div class="info">
<span class="name">Collin J. Doering</span>
<a class="email" href="mailto:collin.doering@rekahsoft.ca">collin.doering@rekahsoft.ca</a>
<a class="gpg-key" href="/files/collin-doering.gpg">4096R/E05BFEC</a>
<a class="email" href="mailto:collin@rekahsoft.ca">collin@rekahsoft.ca</a>
<a class="gpg-key" href="https://rekahsoft.ca/.well-known/openpgpkey/hu/yfzfx1r6gkn5pu7qw1b1ymd1a6wdjkiw">4096R/E05BFEC</a>
</div>
</div>

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-2023</p>
</div>
<div class="six columns omega" id="footer-right">
<p>