1
1
Fork 0

Compare commits

...

2 Commits

Author SHA1 Message Date
Bill Ewanick f0c1084b14 Add seal blog back as it's own thing 2023-08-09 21:08:34 -04:00
Bill Ewanick afa2b9cc1c Delete submodule 2023-08-09 21:07:21 -04:00
99 changed files with 630 additions and 4 deletions

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "nixos-apps/cutesealfanpage.love"]
path = nixos-apps/cutesealfanpage.love
url = https://git.ewanick.com/bill/seal-blog.git

@ -1 +0,0 @@
Subproject commit 31bfab6e11698b7629b8e08febf8af79883ac997

View File

@ -0,0 +1 @@
use flake

View File

@ -0,0 +1,3 @@
[user]
email = admin@cutesealfanpage.love
name = Alice

View File

@ -0,0 +1,15 @@
# Haykll site
_cache
_site
result
# Haskell stuff
site
dist
*.dyn_hi
*.dyn_o
*.hi
*.o
# Direnv lorri stuff
.direnv

View File

@ -0,0 +1,21 @@
{
"git.ignoreLimitWarning": true,
"cSpell.words": [
"cutesealfanpage",
"Dhall",
"Fanpage",
"fmap",
"Hakyll",
"Lookie",
"mappend",
"Monoid",
"Namecheap",
"NOINLINE",
"pandoc",
"runghc",
"uncurry",
"unlines",
"utct"
],
"nix.serverPath": "nil"
}

View File

@ -0,0 +1,39 @@
# Cute Seal Fanpage
> An experiment using [Nix](https://nixos.org/), [Hakyll](https://jaspervdj.be/hakyll/), [Haskell](https://www.haskell.org/), and [shell scripts](https://en.wikipedia.org/wiki/Bourne_shell) to automate a simple website deployment pipeline.
## Why Seals?
It's an in-joke.
## What's all this code?
seal-blog/
- devops/
- build.sh
- Uses a [nix-shell](https://nixos.wiki/wiki/Development_environment_with_nix-shell) expression to build the Hakyll executable
- configuration.nix
- The [configuration file for the NixOS](https://nixos.org/manual/nixos/stable/index.html#ch-configuration) production server. This takes care of installing all the necessary software, setup [Nginx](https://www.nginx.com/), and get [ACME certs](https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment) for verification.
- newSealPost.sh
- Script to be called daily by a cron job on the server. Generates the post for the day, builds and commits.
- website/
- A basic Hakyll site, slightly modified to serve seals. Most of the site is generated from the `site.hs` file. Check the [Hakyll](https://jaspervdj.be/hakyll/) documentation for more info.
## Work to be done
- The blog post generation and the hosting of the website are currently intertwined when they should be separated
- Seal post generator just makes posts
- Hakyll blog imports or calls the post generator
- The deployed server/nix config file has a cron job for adding a new blog and committing every day
- Need to move the blog and post generation inside nixos-apps on my beefier server
- The configuration file here is for it's own Linode, the current small one running, but I have a better setup for that now
- Pull out the useful parts for my deployed server, remove anything not necessary for a small config file
- Also switch to using caddy if not already
- Experiment with `*` A records
- For the seal blog
- Instead of having `www` and `git` and `...` subdomains spelled out in Namecheap
- Just have a `*` record and have caddy do the filtering

View File

@ -0,0 +1,6 @@
---
title: About Me
---
I collect the seal photos
Haven't missed a day yet!

View File

@ -0,0 +1,102 @@
{ sealImagesPath = "images"
, postsOutputPath = "posts"
, startDate = 2022-04-20
, seed = +137
, adjectives1 =
[ "absorbing"
, "adorable"
, "alluring"
, "ambrosial"
, "amiable"
, "appealing"
, "attractive"
, "beautiful"
, "bewitching"
, "captivating"
, "charismatic"
, "charming"
, "choice"
, "cute"
, "dainty"
, "darling"
, "dear"
, "delectable"
, "delicate"
, "delicious"
, "delightful"
, "desirable"
, "dishy"
, "dreamy"
, "electrifying"
, "elegant"
, "enamoring"
, "engaging"
, "engrossing"
, "enthralling"
, "entrancing"
, "eye-catching"
, "fascinating"
, "fetching"
, "glamorous"
, "graceful"
, "heavenly"
, "infatuating"
, "inviting"
, "irresistible"
, "likable"
, "lovable"
, "lovely"
, "magnetizing"
, "nice"
, "pleasant"
, "precious"
, "pretty"
, "provocative"
, "rapturous"
, "ravishing"
, "seducing"
, "seductive"
, "suave"
, "sweet"
, "tantalizing"
, "tempting"
, "titillating"
, "winning"
, "winsome"
]
, adjectives2 =
[ "ample"
, "bearish"
, "big"
, "butterball"
, "buxom"
, "chunky"
, "fatty"
, "flabby"
, "fleshy"
, "full-figured"
, "hefty"
, "husky"
, "pleasingly plump"
, "plump"
, "plumpish"
, "podgy"
, "portly"
, "pudgy"
, "roly-poly"
, "rotund"
, "round"
, "stout"
, "tubby"
, "zaftig"
]
, looks =
[ "Look at"
, "Gaze upon"
, "Check out"
, "Witness!"
, "Look upon and tremble at"
, "Lookie here at"
, "Whoa! See"
]
}

View File

@ -0,0 +1,6 @@
---
title: Contact
---
If you have a seal or seal photos, I'd love to post them!
Send yours to admin AT cutesealfanpage.love

View File

@ -0,0 +1,141 @@
html {
font-size: 62.5%;
}
body {
font-size: 1.6rem;
color: #000;
}
header {
border-bottom: 0.2rem solid #000;
}
nav {
text-align: right;
}
nav a {
font-size: 1.8rem;
font-weight: bold;
color: black;
text-decoration: none;
text-transform: uppercase;
}
footer {
margin-top: 3rem;
padding: 1.2rem 0;
border-top: 0.2rem solid #000;
font-size: 1.2rem;
color: #555;
}
h1 {
font-size: 2.4rem;
}
h2 {
font-size: 2rem;
}
article .header {
font-size: 1.4rem;
font-style: italic;
color: #555;
}
.logo a {
font-weight: bold;
color: #000;
text-decoration: none;
}
@media (max-width: 319px) {
body {
width: 90%;
margin: 0;
padding: 0 5%;
}
header {
margin: 4.2rem 0;
}
nav {
margin: 0 auto 3rem;
text-align: center;
}
footer {
text-align: center;
}
.logo {
text-align: center;
margin: 1rem auto 3rem;
}
.logo a {
font-size: 2.4rem;
}
nav a {
display: block;
line-height: 1.6;
}
}
@media (min-width: 320px) {
body {
width: 90%;
margin: 0;
padding: 0 5%;
}
header {
margin: 4.2rem 0;
}
nav {
margin: 0 auto 3rem;
text-align: center;
}
footer {
text-align: center;
}
.logo {
text-align: center;
margin: 1rem auto 3rem;
}
.logo a {
font-size: 2.4rem;
}
nav a {
display: inline;
margin: 0 0.6rem;
}
}
@media (min-width: 640px) {
body {
width: 60rem;
margin: 0 auto;
padding: 0;
}
header {
margin: 0 0 3rem;
padding: 1.2rem 0;
}
nav {
margin: 0;
text-align: right;
}
nav a {
margin: 0 0 0 1.2rem;
display: inline;
}
footer {
text-align: right;
}
.logo {
margin: 0;
text-align: left;
}
.logo a {
float: left;
font-size: 1.8rem;
}
}

View File

@ -0,0 +1,85 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1691421349,
"narHash": "sha256-RRJyX0CUrs4uW4gMhd/X4rcDG8PTgaaCQM5rXEJOx6g=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "011567f35433879aae5024fc6ec53f2a0568a6c4",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"spg": "spg",
"utils": "utils"
}
},
"spg": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"utils": [
"utils"
]
},
"locked": {
"lastModified": 1691530010,
"narHash": "sha256-4xtMxEstoQ0iM/BOgGhla08xL/DFjS0+Q8nLOl9Gmls=",
"ref": "refs/heads/main",
"rev": "f25dd9c99382fd4e945e4d0d034254fc552d9aca",
"revCount": 14,
"type": "git",
"url": "https://git.ewanick.com/bill/sealPostGenerator.git"
},
"original": {
"type": "git",
"url": "https://git.ewanick.com/bill/sealPostGenerator.git"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1689068808,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@ -0,0 +1,63 @@
{
description = "A Nix flake for my long-running seal blog";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05";
utils.url = "github:numtide/flake-utils";
spg.url = "git+https://git.ewanick.com/bill/sealPostGenerator.git";
spg.inputs.nixpkgs.follows = "nixpkgs";
spg.inputs.utils.follows = "utils";
};
outputs = { self, nixpkgs, utils, ... }@inputs:
utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
ghc' = pkgs.haskellPackages.ghcWithHoogle (self: with self; [
dhall
hakyll
neat-interpolation
random
split
]);
in
rec {
apps.generateSealPosts = {
type = "app";
program = "${inputs.spg.packages.${system}.default}/bin/generateSealPosts";
};
apps.hakyll-site = {
type = "app";
program = "${packages.default}/bin/site";
};
apps.default = apps.hakyll-site;
packages.default = pkgs.stdenv.mkDerivation {
name = "site";
src = self;
buildPhase = ''
${ghc'}/bin/ghc \
-O2 \
-static \
-o site \
site.hs
'';
installPhase = "mkdir -p $out/bin; install -t $out/bin site";
};
devShells.default = pkgs.mkShell {
name = "hakyll-shell";
buildInputs = with pkgs.haskellPackages; [
ghc'
hlint
haskell-language-server
];
};
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

View File

@ -0,0 +1,16 @@
---
title: Seal Blog!
---
<h2>Welcome</h2>
<img src="/images/seal1.jpg" style="float: right; margin: 10px;" width="200px"/>
<p>Welcome to my seal blog!</p>
<p>Here's a list of recent posts for your reading pleasure:</p>
<h2>Seal Posts</h2>
$partial("templates/post-list.html")$
<p>…or you can find more in the <a href="/archive.html">archives</a>.</p>

View File

@ -0,0 +1,68 @@
--------------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
import Data.Monoid (mappend)
import Hakyll
--------------------------------------------------------------------------------
main :: IO ()
main = hakyll $ do
match "images/*" $ do
route idRoute
compile copyFileCompiler
match "css/*" $ do
route idRoute
compile compressCssCompiler
match (fromList ["about.rst", "contact.markdown"]) $ do
route $ setExtension "html"
compile $ pandocCompiler
>>= loadAndApplyTemplate "templates/default.html" defaultContext
>>= relativizeUrls
match "posts/*" $ do
route $ setExtension "html"
compile $ pandocCompiler
>>= loadAndApplyTemplate "templates/post.html" postCtx
>>= loadAndApplyTemplate "templates/default.html" postCtx
>>= relativizeUrls
create ["archive.html"] $ do
route idRoute
compile $ do
posts <- recentFirst =<< loadAll "posts/*"
let archiveCtx =
listField "posts" postCtx (return posts) `mappend`
constField "title" "Archives" `mappend`
defaultContext
makeItem ""
>>= loadAndApplyTemplate "templates/archive.html" archiveCtx
>>= loadAndApplyTemplate "templates/default.html" archiveCtx
>>= relativizeUrls
match "index.html" $ do
route idRoute
compile $ do
posts <- fmap (take 20) . recentFirst =<< loadAll "posts/*"
-- posts <- recentFirst =<< loadAll "posts/*"
let indexCtx =
listField "posts" postCtx (return posts) `mappend`
constField "title" "Home" `mappend`
defaultContext
getResourceBody
>>= applyAsTemplate indexCtx
>>= loadAndApplyTemplate "templates/default.html" indexCtx
>>= relativizeUrls
match "templates/*" $ compile templateBodyCompiler
--------------------------------------------------------------------------------
postCtx :: Context String
postCtx =
dateField "date" "%B %e, %Y" `mappend`
defaultContext

View File

@ -0,0 +1,2 @@
Here you can find all my previous posts:
$partial("templates/post-list.html")$

View File

@ -0,0 +1,33 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My Seal Blog - $title$</title>
<link rel="stylesheet" href="/css/default.css" />
</head>
<body>
<header>
<div class="logo">
<a href="/">My Seal Blog</a>
</div>
<nav>
<a href="/">Home</a>
<a href="/about.html">About</a>
<a href="/contact.html">Contact</a>
<a href="/archive.html">Archive</a>
</nav>
</header>
<main role="main">
<h1>$title$</h1>
$body$
</main>
<footer>
Site proudly generated by
<a href="http://jaspervdj.be/hakyll">Hakyll</a>
</footer>
</body>
</html>

View File

@ -0,0 +1,7 @@
<ul>
$for(posts)$
<li>
<a href="$url$">$title$</a> - $date$
</li>
$endfor$
</ul>

View File

@ -0,0 +1,11 @@
<article>
<section class="header">
Posted on $date$
$if(author)$
by $author$
$endif$
</section>
<section>
$body$
</section>
</article>

View File

@ -0,0 +1,11 @@
name: website
version: 0.1.0.0
build-type: Simple
cabal-version: >= 1.10
executable site
main-is: site.hs
build-depends: base == 4.*
, hakyll == 4.13.*
ghc-options: -threaded
default-language: Haskell2010