summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.envrc1
-rw-r--r--.gitignore2
-rw-r--r--BACKLOG.md43
-rw-r--r--README.md5
-rw-r--r--ads.txt1
-rw-r--r--app-ads.txt1
-rw-r--r--cabal.project.freeze2
-rw-r--r--flake.lock61
-rw-r--r--flake.nix79
-rw-r--r--haskell-toolchain1
-rw-r--r--posts/brck.md2
-rw-r--r--site.hs167
-rw-r--r--site.nix17
13 files changed, 298 insertions, 84 deletions
diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/.gitignore b/.gitignore
index af1c5f6..f951e20 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+/result
+/.direnv
.stack-work
_cache
_site
diff --git a/BACKLOG.md b/BACKLOG.md
new file mode 100644
index 0000000..b267574
--- /dev/null
+++ b/BACKLOG.md
@@ -0,0 +1,43 @@
+# Backlog
+
+## Posts
+
+- My `~/bin` scripts and wrappers.
+- OpenBSD hooks: rc.firsttime(8), upgrade.site(8), apmd(8) /etc/apm/suspend, crontab(5) @reboot, etc.
+- Auto-install Arch Linux on vmm (unstable, at least on Intel hardware).
+- Checkout recently modified Git branch using fzf (git-co).
+- Portable sftp server/sync using config-less rclone.
+- shellpass, shellpass-ssh, shellpass-fzf, keybindings for cwm and tmux.
+- My smux ssh wrapper.
+- Hacked vipe clone: vi-pipe.
+- tmux-fzf-ksh integration: search file system, history, password-store.
+- Dotfiles under revision control: How I manage my personal configuration files with Git.
+- My preferred tools: A whirlwind tour of my preferred command-line tools.
+- SSH proxy: How to establish a secure tunnel between two firewalled machines.
+- Your favorite Newsletter may track you.
+- Migrate PostgreSQL schema without downtime (with zero downtime) using triggers.
+- Inline HTML images with this simple script.
+- Noscript photo gallery with pure CSS carousel, responsive images and deep links.
+- SQLite hexastore vs. Neo4j.
+- Setup a FreeBSD sftp server with basic email notifications from the command-line using bash, ssh, and the DigitalOcean command-line application
+- Cloud/VPS provider comparison
+- Practical, cross-platform, append-only backups using restic, rclone, and ssh: ready to use scripts/shortcuts for OpenBSD, Linux, macOS, Windows 10.
+- Find man(1) pages interactively using fzf(1), see ~/.kshrc.
+- Soft datasize limit plus temporary swap file, see Kapho README.
+- Analyze Tokio Tracing logs with jq, see Monikas Kochbuch.
+- Compile SQLite3 extensions (uuid) and tools (sqldiff) from source, see Monikas Kochbuch.
+- Prevent browsers from requesting a non-existing /favicon.ico.
+- Bidirectional sync (via untrusted server).
+- Mount external OpenBSD softraid crypto volume at boot.
+
+## Other content
+
+- Review privacy policy. See also <https://privsec.dev/privacy/>.
+- Note limited availability due to home ISP and bare metal mini PC. Link to Internet Archive.
+
+## Implementation
+
+- Check out [Zola](https://www.getzola.org/documentation/getting-started/overview/).
+- Revisit [CSS nesting support](https://caniuse.com/css-nesting).
+- Add Favicon.
+- Add [Open Graph](https://ogp.me/) meta data.
diff --git a/README.md b/README.md
index 714ed8b..2544443 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,11 @@ Deploy the website:
$ cabal v2-run exe:site -- deploy
+Alternatively build the experimental Nix flake:
+
+ $ nix build git+https://git.skreutz.com/blog.git
+ $ open ./result/srv/www.skreutz.com/index.html
+
## Credits
Built with [Hakyll](https://jaspervdj.be/hakyll/) and
diff --git a/ads.txt b/ads.txt
new file mode 100644
index 0000000..19ce4ca
--- /dev/null
+++ b/ads.txt
@@ -0,0 +1 @@
+placeholder.example.com, placeholder, DIRECT, placeholder
diff --git a/app-ads.txt b/app-ads.txt
new file mode 100644
index 0000000..19ce4ca
--- /dev/null
+++ b/app-ads.txt
@@ -0,0 +1 @@
+placeholder.example.com, placeholder, DIRECT, placeholder
diff --git a/cabal.project.freeze b/cabal.project.freeze
index 5aea528..ad2aaf9 100644
--- a/cabal.project.freeze
+++ b/cabal.project.freeze
@@ -1 +1 @@
-index-state: hackage.haskell.org 2025-05-24T14:41:27Z
+index-state: hackage.haskell.org 2025-09-02T10:46:24Z
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..f5ff82b
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,61 @@
+{
+ "nodes": {
+ "flake-parts": {
+ "inputs": {
+ "nixpkgs-lib": "nixpkgs-lib"
+ },
+ "locked": {
+ "lastModified": 1763759067,
+ "narHash": "sha256-LlLt2Jo/gMNYAwOgdRQBrsRoOz7BPRkzvNaI/fzXi2Q=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "2cccadc7357c0ba201788ae99c4dfa90728ef5e0",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1755829505,
+ "narHash": "sha256-4/Jd+LkQ2ssw8luQVkqVs9spDBVE6h/u/hC/tzngsPo=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "f937f8ecd1c70efd7e9f90ba13dfb400cf559de4",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-lib": {
+ "locked": {
+ "lastModified": 1761765539,
+ "narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=",
+ "owner": "nix-community",
+ "repo": "nixpkgs.lib",
+ "rev": "719359f4562934ae99f5443f20aa06c2ffff91fc",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "nixpkgs.lib",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-parts": "flake-parts",
+ "nixpkgs": "nixpkgs"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..2bde98d
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,79 @@
+{
+ description = "Stefan Kreutz's personal website";
+
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+ flake-parts.url = "github:hercules-ci/flake-parts";
+ };
+
+ outputs =
+ inputs@{
+ self,
+ nixpkgs,
+ flake-parts,
+ ...
+ }:
+ flake-parts.lib.mkFlake { inherit inputs; } {
+ systems = [
+ "x86_64-linux"
+ "aarch64-linux"
+ "x86_64-darwin"
+ "aarch64-darwin"
+ ];
+
+ perSystem =
+ { pkgs, system, ... }:
+ let
+ src = builtins.path {
+ path = ./.;
+ name = "blog";
+ };
+ in
+ rec {
+ # nix develop
+ devShells.default = pkgs.mkShell {
+ nativeBuildInputs = with pkgs; [
+ ghc
+ cabal-install
+ lychee
+ zlib
+ ];
+ };
+
+ # nix build .
+ # nix run nixpkgs#python3 -- -m http.server --directory ./result/srv/www.skreutz.com --bind 127.0.0.1 8080
+ # open http://127.0.0.1:8080/
+ packages.default = packages.site;
+
+ # nix run .#generator -- --help
+ packages.generator = pkgs.haskell.lib.compose.justStaticExecutables (
+ pkgs.haskellPackages.callCabal2nix "blog" src { }
+ );
+
+ # nix build .#site
+ packages.site = pkgs.callPackage ./site.nix {
+ inherit self;
+ inherit (packages) generator;
+ };
+
+ # nix flake check
+ checks.links = pkgs.stdenvNoCC.mkDerivation {
+ inherit src;
+ name = "offline-link-check";
+ dontBuild = true;
+ doCheck = true;
+ nativeBuildInputs = with pkgs; [ lychee ];
+ checkPhase = ''
+ "${packages.generator}/bin/site" rebuild
+ lychee --offline --no-progress --require-https --root-dir "$( realpath _site )" _site
+ '';
+ installPhase = ''
+ mkdir $out
+ '';
+ };
+
+ # nix fmt
+ formatter = pkgs.nixfmt-tree;
+ };
+ };
+}
diff --git a/haskell-toolchain b/haskell-toolchain
index 0029649..1a65f8d 100644
--- a/haskell-toolchain
+++ b/haskell-toolchain
@@ -1,3 +1,4 @@
+# WARNING: This file is out of date.
# OpenBSD 7.7 port versions.
# WORKAROUND: No GHC 9.8.3 available. Using 9.8.2 instead.
GHC_VERSION=9.8.2
diff --git a/posts/brck.md b/posts/brck.md
index 3913e17..04160a9 100644
--- a/posts/brck.md
+++ b/posts/brck.md
@@ -107,7 +107,7 @@ See the built-in help for all supported options:
[brck-repo]: https://git.skreutz.com/brck.git/
[brck-cratesio]: https://crates.io/crates/brck
[cargo]: https://doc.rust-lang.org/cargo/
-[jq]: https://jqlang.github.io/jq/
+[jq]: https://jqlang.org/
[pledge]: https://man.openbsd.org/pledge
[unveil]: https://man.openbsd.org/unveil
[jargon]: http://www.catb.org/jargon/html/B/bit-rot.html
diff --git a/site.hs b/site.hs
index c016f04..7d4b4b0 100644
--- a/site.hs
+++ b/site.hs
@@ -8,90 +8,93 @@ import System.FilePath.Posix ((</>), (<.>), splitExtension, splitFileName, takeD
import Text.Pandoc (Pandoc, Inline(Link, Space, Str), Block(Header))
import Text.Pandoc.Shared (stringify)
import Text.Pandoc.Walk (walk)
+import qualified GHC.IO.Encoding as Encoding
main :: IO ()
-main = hakyllWith hakyllConfig $ do
- match ("images/*" .||. "files/*" .||. "robots.txt" .||. "health") $ do
- route idRoute
- compile copyFileCompiler
-
- match "css/*" $ do
- route idRoute
- -- WORKAROUND: compressCssCompiler removes copyright notices
- compile copyFileCompiler
-
- match (fromList ["about.md", "contact.md", "privacy.md", "code.md"]) $ do
- route $ setExtension "html" `composeRoutes` appendIndex
- let context = dropIndexHtml "url" <> defaultContext
- compile $ customPandocCompiler
- >>= loadAndApplyTemplate "templates/direct.html" context
- >>= loadAndApplyTemplate "templates/default.html" context
-
- match "posts/*" $ do
- route $ setExtension "html" `composeRoutes` appendIndex
- compile $ customPandocCompiler
- >>= loadAndApplyTemplate "templates/post.html" postContext
- >>= saveSnapshot "content"
- >>= loadAndApplyTemplate "templates/default.html" postContext
-
- create ["posts.html"] $ do
- route appendIndex
- compile $ do
- posts <- recentFirst =<< loadAll "posts/*"
- let archiveContext =
- listField "posts" postContext (return posts) <>
- constField "title" "Blog" <>
- dropIndexHtml "url" <>
- defaultContext
-
- makeItem ""
- >>= loadAndApplyTemplate "templates/posts.html" archiveContext
- >>= loadAndApplyTemplate "templates/default.html" archiveContext
-
- create ["feeds/posts.rss"] $ do
- route idRoute
- compileFeed renderRss rfc822DateTimeFormat
-
- create ["feeds/posts.atom"] $ do
- route idRoute
- compileFeed renderAtom rfc3339DateTimeFormat
-
- create ["sitemap.xml"] $ do
- route idRoute
- compile $ do
- posts <- recentFirst =<< loadAll "posts/*"
- singles <- loadAll (fromList ["about.md", "contact.md", "privacy.md", "code.md", "posts.html"])
- let
- pages = posts <> singles
- sitemapContext =
- constField "root" root <>
- listField "pages" postContext (return pages)
- makeItem ""
- >>= loadAndApplyTemplate "templates/sitemap.xml" sitemapContext
-
- match "index.html" $ do
- route idRoute
- compile $ do
- posts <- recentFirst =<< loadAll "posts/*"
- let indexContext =
- listField "posts" postContext (return posts) <>
- defaultContext
-
- getResourceBody
- >>= applyAsTemplate indexContext
- >>= loadAndApplyTemplate "templates/default.html" indexContext
-
- match "err.html" $ do
- route idRoute
- compile $ customPandocCompiler
- >>= loadAndApplyTemplate "templates/default.html" defaultContext
-
- match "404.html" $ do
- route idRoute
- compile $ customPandocCompiler
- >>= loadAndApplyTemplate "templates/default.html" defaultContext
-
- match "templates/*" $ compile templateBodyCompiler
+main = do
+ Encoding.setLocaleEncoding Encoding.utf8
+ hakyllWith hakyllConfig $ do
+ match ("images/*" .||. "files/*" .||. "robots.txt" .||. "ads.txt" .||. "app-ads.txt" .||. "health") $ do
+ route idRoute
+ compile copyFileCompiler
+
+ match "css/*" $ do
+ route idRoute
+ -- WORKAROUND: compressCssCompiler removes copyright notices
+ compile copyFileCompiler
+
+ match (fromList ["about.md", "contact.md", "privacy.md", "code.md"]) $ do
+ route $ setExtension "html" `composeRoutes` appendIndex
+ let context = dropIndexHtml "url" <> defaultContext
+ compile $ customPandocCompiler
+ >>= loadAndApplyTemplate "templates/direct.html" context
+ >>= loadAndApplyTemplate "templates/default.html" context
+
+ match "posts/*" $ do
+ route $ setExtension "html" `composeRoutes` appendIndex
+ compile $ customPandocCompiler
+ >>= loadAndApplyTemplate "templates/post.html" postContext
+ >>= saveSnapshot "content"
+ >>= loadAndApplyTemplate "templates/default.html" postContext
+
+ create ["posts.html"] $ do
+ route appendIndex
+ compile $ do
+ posts <- recentFirst =<< loadAll "posts/*"
+ let archiveContext =
+ listField "posts" postContext (return posts) <>
+ constField "title" "Blog" <>
+ dropIndexHtml "url" <>
+ defaultContext
+
+ makeItem ""
+ >>= loadAndApplyTemplate "templates/posts.html" archiveContext
+ >>= loadAndApplyTemplate "templates/default.html" archiveContext
+
+ create ["feeds/posts.rss"] $ do
+ route idRoute
+ compileFeed renderRss rfc822DateTimeFormat
+
+ create ["feeds/posts.atom"] $ do
+ route idRoute
+ compileFeed renderAtom rfc3339DateTimeFormat
+
+ create ["sitemap.xml"] $ do
+ route idRoute
+ compile $ do
+ posts <- recentFirst =<< loadAll "posts/*"
+ singles <- loadAll (fromList ["about.md", "contact.md", "privacy.md", "code.md", "posts.html"])
+ let
+ pages = posts <> singles
+ sitemapContext =
+ constField "root" root <>
+ listField "pages" postContext (return pages)
+ makeItem ""
+ >>= loadAndApplyTemplate "templates/sitemap.xml" sitemapContext
+
+ match "index.html" $ do
+ route idRoute
+ compile $ do
+ posts <- recentFirst =<< loadAll "posts/*"
+ let indexContext =
+ listField "posts" postContext (return posts) <>
+ defaultContext
+
+ getResourceBody
+ >>= applyAsTemplate indexContext
+ >>= loadAndApplyTemplate "templates/default.html" indexContext
+
+ match "err.html" $ do
+ route idRoute
+ compile $ customPandocCompiler
+ >>= loadAndApplyTemplate "templates/default.html" defaultContext
+
+ match "404.html" $ do
+ route idRoute
+ compile $ customPandocCompiler
+ >>= loadAndApplyTemplate "templates/default.html" defaultContext
+
+ match "templates/*" $ compile templateBodyCompiler
hakyllConfig :: Configuration
hakyllConfig = defaultConfiguration
diff --git a/site.nix b/site.nix
new file mode 100644
index 0000000..1988b49
--- /dev/null
+++ b/site.nix
@@ -0,0 +1,17 @@
+{
+ self,
+ stdenv,
+ generator,
+}:
+
+stdenv.mkDerivation rec {
+ name = "www.skreutz.com";
+ src = self;
+ buildPhase = ''
+ ${generator}/bin/site rebuild
+ '';
+ installPhase = ''
+ mkdir -p $out/srv
+ cp -a _site $out/srv/${name}
+ '';
+}
Generated by cgit. See skreutz.com for my tech blog and contact information.