Compare commits
52 Commits
368d1c4880
...
1c5aeba23e
Author | SHA1 | Date |
---|---|---|
Collin J. Doering | 1c5aeba23e | |
Collin J. Doering | 7fc316a171 | |
Collin J. Doering | b9ee2f8266 | |
Collin J. Doering | 5c4138d4d4 | |
Collin J. Doering | 9b167b4d9d | |
Collin J. Doering | 880978c9a1 | |
Collin J. Doering | f11fbdd3f4 | |
Collin J. Doering | ec708bcf29 | |
Collin J. Doering | 5d00301c86 | |
Collin J. Doering | b162752ce6 | |
Collin J. Doering | bc19c03d75 | |
Collin J. Doering | 6a00da6970 | |
Collin J. Doering | 920fe7d9f4 | |
Collin J. Doering | 5c2f33456e | |
Collin J. Doering | 46d25cd912 | |
Collin J. Doering | 1dae28b6f4 | |
Collin J. Doering | b1476560ec | |
Collin J. Doering | 0b47b38f61 | |
Collin J. Doering | 1e1ae99446 | |
Collin J. Doering | ee09fc99b8 | |
Collin J. Doering | 5ba926b5d2 | |
Collin J. Doering | 4b18054324 | |
Collin J. Doering | 29dc67c881 | |
Collin J. Doering | dd6434b4ee | |
Collin J. Doering | fd15b80b62 | |
Collin J. Doering | ff1d13a670 | |
Collin J. Doering | e69f751011 | |
Collin J. Doering | 50f14dfa8d | |
Collin J. Doering | c9ad841f53 | |
Collin J. Doering | fc40cc18d1 | |
Collin J. Doering | 3ebc0b1bbd | |
Collin J. Doering | fc2fe49022 | |
Collin J. Doering | 5dfb1b88ee | |
Collin J. Doering | 10a72486a5 | |
Collin J. Doering | d6464b8ac7 | |
Collin J. Doering | 6c7fa76f4a | |
Collin J. Doering | 8f47caede9 | |
Collin J. Doering | 7d59eb100b | |
Collin J. Doering | b1875dfc27 | |
Collin J. Doering | df02a2a62d | |
Collin J. Doering | a1dbf95c49 | |
Collin J. Doering | b40ff4f4ad | |
Collin J. Doering | 2f3d052cd6 | |
Collin J. Doering | 7d679dd94b | |
Collin J. Doering | a15def12bc | |
Collin J. Doering | d3ae74879b | |
Collin J. Doering | b22b7e04b4 | |
Collin J. Doering | 2620a711fb | |
Collin J. Doering | 06e473583a | |
Collin J. Doering | b0cb08f624 | |
Collin J. Doering | f691f99210 | |
Collin J. Doering | 94e74df3be |
|
@ -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"),
|
||||
])
|
||||
]
|
121
.drone.yml
121
.drone.yml
|
@ -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
|
||||
|
||||
...
|
||||
|
|
|
@ -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
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -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
|
||||
|
|
88
README.md
88
README.md
|
@ -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/
|
|
@ -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]].
|
|
@ -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.
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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")))))
|
|
@ -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
|
|
@ -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 |
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../channels.scm
|
|
@ -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};
|
||||
|
|
|
@ -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"))
|
|
@ -25,3 +25,6 @@ variable "enable_naked_domain" {
|
|||
default = false
|
||||
}
|
||||
|
||||
variable "site_static_files_dir" {
|
||||
default = "../_site"
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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
24
site
|
@ -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
|
||||
|
|
288
src/site.hs
288
src/site.hs
|
@ -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 --------------------------------------------------------------------------------
|
||||
|
|
65
stack.yaml
65
stack.yaml
|
@ -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
|
|
@ -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
|
|
@ -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>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="container">
|
||||
<footer>
|
||||
<div class="six columns alpha" id="footer-left">
|
||||
<p>© Collin Doering 2016</p>
|
||||
<p>© Collin Doering 2016-2021</p>
|
||||
</div>
|
||||
<div class="six columns omega" id="footer-right">
|
||||
<p>
|
||||
|
|
Loading…
Reference in New Issue