Initial Hakyll and Nix commit

main
Bill Ewanick 2020-08-14 19:01:29 -04:00
commit c83f4c78d5
16 changed files with 755 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
_cache
_site
result
site
site.hi
site.o

6
about.rst Normal file
View File

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

3
build.sh Executable file
View File

@ -0,0 +1,3 @@
nix-shell --pure -p \
"haskellPackages.ghcWithPackages (pkgs: with pkgs; [ hakyll ])" \
--command 'ghc --make site'

116
configuration.nix Normal file
View File

@ -0,0 +1,116 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running nixos-help).
{ config, pkgs, ... }:
{
# List packages installed in system profile. To search, run:
# $ nix search wget
environment.systemPackages = with pkgs; [
inetutils
mtr
sysstat
# For ACME Certificates
certbot
];
#######################################
# Security/networking important configs
#######################################
# Open ports for our domain
networking = {
domain = "cutesealfanpage.love";
firewall.allowedTCPPorts = [ 80 443 ];
};
# Add acme LetsEncrypt certs
security.acme = {
acceptTerms = true;
validMinDays = 999;
email = "admin@cutesealfanpage.love";
# uncomment this to use the staging server
server = "https://acme-staging-v02.api.letsencrypt.org/directory";
};
#######################################
# List services that you want to enable
#######################################
services = {
# Enable the OpenSSH daemon.
openssh = {
enable = true;
permitRootLogin = "no";
};
# Enable the Longview Agent
# https://www.linode.com/docs/tools-reference/custom-kernels-distros/install-nixos-on-linode/#enable-longview-agent-optional
longview = {
enable = true;
apiKeyFile = "/var/lib/longview/apiKeyFile";
};
# Nginx config
nginx = {
enable = true;
statusPage = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
# https://nixos.org/nixos/manual/#module-security-acme-nginx
virtualHosts = {
"cutesealfanpage.love" = {
enableACME = true;
forceSSL = true;
locations."/" = { proxyPass = "http://127.0.0.1:8000"; };
};
};
};
};
#######################################
# Define a user account. Don't forget to set a password with passwd.
#######################################
users.users.alice = {
isNormalUser = true;
home = "/home/alice";
description = "Alice Foobar";
extraGroups = [
"wheel" # Enable sudo for the user.
"networkmanager"
];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMeBlGnpJ7dTVcrDdYlMsXFhADIYLc4K/acgsxwbZPOA alice@foobar"
];
};
#######################################
# NixOS / Linode special options and/or incancations
#######################################
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. Its perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "20.03"; # Did you read the comment?
imports = [ # Include the results of the hardware scan.
./hardware-configuration.nix
];
# special options and/or incancations
boot.loader.grub.enable = true;
boot.loader.grub.version = 2;
networking.useDHCP = false;
networking.interfaces.eth0.useDHCP = true;
networking.usePredictableInterfaceNames = false;
}

6
contact.markdown Normal file
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

141
css/default.css Normal file
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;
}
}

25
default.nix Normal file
View File

@ -0,0 +1,25 @@
{ pkgs ? import <nixpkgs> { } }:
pkgs.stdenv.mkDerivation rec {
pname = "test-blog";
version = "latest";
src = "./${pname}";
phases = "installPhase";
buildInputs =
[ "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ hakyll ])" ];
installPhase = ''
echo Install Phase
'';
buildPhase = ''
ghc --make site
'';
}
pkgs.run-command "my-example" {} ''
echo My example command is running
pwd
cd test-blog
./site watch
''

83
deploy.sh Executable file
View File

@ -0,0 +1,83 @@
# Deploy script
#
# Run with
# $ sudo ./deploy.sh
# and enter password
# Change up user and IP as appropriate
# for each site
# Add this for dry-run:
# --dry-run \
# time added for timing at the end. Check Real time
echo "rsycing the blog to remote"
echo "please enter remote alice password:"
time rsync \
--recursive \
--update \
--executability \
--times \
--compress \
--stats \
--rsh=ssh \
../seal-blog \
alice@172.105.4.234:/home/alice
# Move the configuration script
# scp configuration.nix alice@172.105.4.234:/etc/nixos/configuration.nix
# Restart NixOS
# ssh -t alice@172.105.4.234 sudo nixos-rebuild switch
# Kill the website
# ssh -t alice@172.105.4.234 pkill site
# Build the site
# ssh -t alice@172.105.4.234
echo -e "\n\nCopy files over finished!"
echo -e "\n\n\nAlice needs to do some steps"
echo "Enter her password so she can get to work:"
ssh -t alice@172.105.4.234 "
alias echo='echo -e'
echo 'Now enter her password for sudo inside the ssh'
sudo echo 'echo ULTIMATE POWER'
echo '\nMake sure we know where we are'
ls
pwd
echo '\ncd seal-blog'
cd seal-blog
echo 'copy the configuration over'
echo 'TODO: make this only if changed'
sudo mv configuration.nix /etc/nixos/configuration.nix
echo '\nRebuild NixOS'
echo 'TODO: make this only if changed'
echo sudo nixos-rebuild switch
echo '\npkill the site'
echo 'TODO: make this only if necessary'
echo pkill site
echo '\nBuild the site'
echo 'TODO: shouldnt need to build on deploy server'
echo ./build.sh
echo 'Start the site in the background using nohup'
echo 'TODO: make this only if necessary'
echo nohup 'site watch' >>/dev/null 2>>/dev/null &
echo site watch &
echo disown
echo '\n\nDeploy finished!\n\n'
"
# on alice
# ssh -t alice@172.105.4.234
# in a week (August 21st)
# need to run `sudo certbot certonly`
# get the cert for the site, and set it in nginx
# nohup
# https://superuser.com/a/38567
# https://en.wikipedia.org/wiki/Nohup

11
ewanick-blog.cabal Normal file
View File

@ -0,0 +1,11 @@
name: seal-blog
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

222
generateSealPosts.hs Normal file
View File

@ -0,0 +1,222 @@
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
-- enter in repl:
-- :set -XOverloadedStrings
import System.IO.Unsafe ( unsafePerformIO )
import System.Random ( randomRIO )
import System.Directory ( listDirectory )
import Control.Monad ( forM, forM_, replicateM )
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Data.List
import Data.Time.Calendar
import Data.Time.Clock
import NeatInterpolation
-- https://hackage.haskell.org/package/neat-interpolation-0.3.2.1/docs/NeatInterpolation.html
{-
Gives a random number between from and to
Uses unsafeIO to get the number out of IO
It's safe because we're only shuffling
-}
randomNum from to =
unsafePerformIO $
randomRIO (from, to)
{-
Given a list, returns a random element
-}
randomPull lst = lst !! r'
where r' = randomNum 0 l
l = length lst - 1
blogPost
:: Text
-> Text
-> Text
-> Text
-> Text
-> Day
-> Text
blogPost title see adj1 adj2 seal date =
[text|
---
title: $title
---
$see this $adj1, $adj2 seal!
<img
src="/images/$seal"
alt="A picture of a $adj1, $adj2 seal! <3"
width="400"
/>
|]
-- Returns a filePath, and a corresponding random blog post
sealText :: Integer -> Day -> (FilePath, Text)
sealText n date = ( fileName', blogPost')
where
fileName' =
show date ++ "-"
++ "seal-post-"
++ show n
++ ".markdown"
date' = T.pack . show $ date
title = T.pack $ "Seal Post Number " ++ show n
title' = T.replace " " "-" title
blogPost' =
blogPost
title
(randomPull looks)
(randomPull adjectives)
(randomPull adjectives')
(randomPull sealImages)
date
-- Generating all the previous blog posts
-- Only need to do this once
-- Another function takes care of creating today's blog post
startDate = fromGregorian 1998 06 11
-- startDate = fromGregorian 2020 07 31
{-# NOINLINE today #-}
today = unsafePerformIO $ utctDay <$> getCurrentTime
daysSinceStart = diffDays today startDate
allDatesSinceStart = map (`addDays` startDate) [1..daysSinceStart]
allBlogPosts = map f zipped
where
f = uncurry sealText
zipped = zip [1..] allDatesSinceStart
writeToFile (fp, txt) = write fp' txt
where
write = TIO.writeFile
fp' = "posts/" ++ fp
-- For all the blog posts
-- Write them to file
unsafeGenerateAllBlogs = forM_ allBlogPosts writeToFile
prettyPrint :: Show a => [a] -> IO ()
prettyPrint = putStr . unlines . map show
{-
Adjectives
-}
adjectives :: [Text]
adjectives =
[ "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"
]
adjectives' :: [Text]
adjectives' =
[ "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 :: [Text]
looks =
[ "Look at"
, "Gaze upon"
, "Check out"
, "Witness!"
, "Look upon and tremble at"
, "Lookie here at"
, "Whoa! See"
]
sealImages :: [Text]
sealImages = map T.pack
. sort
. unsafePerformIO
$ listDirectory "images/seals"

16
index.html Normal file
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>

67
site.hs Normal file
View File

@ -0,0 +1,67 @@
--------------------------------------------------------------------------------
{-# 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 <- 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

2
templates/archive.html Normal file
View File

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

33
templates/default.html Normal file
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>

7
templates/post-list.html Normal file
View File

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

11
templates/post.html Normal file
View File

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