diff options
| -rw-r--r-- | .envrc | 1 | ||||
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | BACKLOG.md | 43 | ||||
| -rw-r--r-- | README.md | 5 | ||||
| -rw-r--r-- | ads.txt | 1 | ||||
| -rw-r--r-- | app-ads.txt | 1 | ||||
| -rw-r--r-- | cabal.project.freeze | 2 | ||||
| -rw-r--r-- | flake.lock | 61 | ||||
| -rw-r--r-- | flake.nix | 79 | ||||
| -rw-r--r-- | haskell-toolchain | 1 | ||||
| -rw-r--r-- | posts/brck.md | 2 | ||||
| -rw-r--r-- | site.hs | 167 | ||||
| -rw-r--r-- | site.nix | 17 |
13 files changed, 298 insertions, 84 deletions
@@ -0,0 +1 @@ +use flake @@ -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. @@ -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 @@ -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 @@ -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} + ''; +} |