From 43a4ee6234d3a38d6f9853b53c3d03cc67cd3287 Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Tue, 22 Oct 2024 18:49:51 -0700 Subject: Write a blog post. --- roseh.moe.go | 123 +++++++++++++++++++-- templates/blog/post.html.template | 4 + .../blog/posts/2024-10-22_nix_flakes.html.template | 82 ++++++++++++++ templates/index.html.template | 34 +++--- templates/outline.html.template | 11 ++ 5 files changed, 225 insertions(+), 29 deletions(-) create mode 100644 templates/blog/post.html.template create mode 100644 templates/blog/posts/2024-10-22_nix_flakes.html.template create mode 100644 templates/outline.html.template diff --git a/roseh.moe.go b/roseh.moe.go index 5ce4258..ad09cb5 100644 --- a/roseh.moe.go +++ b/roseh.moe.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "embed" "flag" "fmt" @@ -10,6 +11,7 @@ import ( "net/http" "net/http/httputil" "net/url" + "slices" "strings" "time" ) @@ -36,26 +38,126 @@ func notFound(w http.ResponseWriter, r *http.Request) { } } +//go:embed templates/outline.html.template +var outlineString string + +var outlineTemplate = template.Must(template.New("outline").Parse(outlineString)) + +var ( + //go:embed templates/blog/post.html.template + postString string + + //go:embed templates/blog/posts + blogPostFiles embed.FS +) + +func preloadBlogPosts() map[string][]byte { + base, err := outlineTemplate.Clone() + if err != nil { + log.Fatalf("outlineTemplate.Clone: %s", err) + } + base, err = base.New("body").Parse(postString) + if err != nil { + log.Fatalf("post.html.template: invalid template: %s", err) + } + postTemplates, err := blogPostFiles.ReadDir("templates/blog/posts") + if err != nil { + log.Fatalf("blog posts: %s", err) + } + posts := make(map[string][]byte, len(postTemplates)) + for _, post := range postTemplates { + base, err = base.Clone() + if err != nil { + log.Fatalf("base.Clone: %s", err) + } + postString, err := blogPostFiles.ReadFile("templates/blog/posts/" + post.Name()) + if err != nil { + log.Fatalf("somehow I fucked the paths up: %s", err) + } + postTemplate, err := base.New("post").Parse(string(postString)) + if err != nil { + log.Fatalf("%q: invalid template: %s", post.Name(), err) + } + buf := new(bytes.Buffer) + if err := postTemplate.ExecuteTemplate(buf, "outline", nil); err != nil { + log.Fatalf("%q: error: %s", post.Name(), err) + } + posts[strings.TrimSuffix(post.Name(), ".html.template")] = buf.Bytes() + } + return posts +} + +var blogPostsHTML = preloadBlogPosts() + +func blog(w http.ResponseWriter, r *http.Request) { + post, ok := blogPostsHTML[strings.TrimPrefix(r.URL.Path, "/blog/")] + if !ok { + notFound(w, r) + return + } + http.ServeContent(w, r, "post.html", serverStartTime, bytes.NewReader(post)) +} + //go:embed templates/index.html.template var indexString string -var indexTemplate = template.Must(template.New("index").Parse(indexString)) - -func index(w http.ResponseWriter, _ *http.Request) { - if err := indexTemplate.Execute(w, nil); err != nil { - log.Printf("Warning: index: %s", err) +func preloadIndex() []byte { + base, err := outlineTemplate.Clone() + if err != nil { + log.Fatalf("outlineTemplate.Clone: %s", err) + } + base.Funcs(template.FuncMap{ + "name": func(post string) string { + postBytes, err := blogPostFiles.ReadFile("templates/blog/posts/" + post + ".html.template") + if err != nil { + log.Fatalf("no such post %q: %s", post, err) + } + date := post[:strings.IndexByte(post, '_')] + firstLine := postBytes[:bytes.IndexByte(postBytes, '\n')] + return date + ": " + string(bytes.TrimSuffix(bytes.TrimPrefix(firstLine, []byte("

")), []byte("

"))) + }, + }) + indexTemplate, err := base.New("body").Parse(indexString) + if err != nil { + log.Fatalf("index.html.template: invalid template: %s", err) + } + posts := make([]string, 0, len(blogPostsHTML)) + for p := range blogPostsHTML { + posts = append(posts, p) } + slices.Sort(posts) + buf := new(bytes.Buffer) + if err := indexTemplate.ExecuteTemplate(buf, "outline", posts); err != nil { + log.Fatalf("index.html.template: error: %s", err) + } + return buf.Bytes() +} + +var indexHTML = preloadIndex() + +func index(w http.ResponseWriter, r *http.Request) { + http.ServeContent(w, r, "index.html", serverStartTime, bytes.NewReader(indexHTML)) } //go:embed templates/pong.html.template var pongString string -var pongTemplate = template.Must(template.New("pong").Parse(pongString)) - -func pong(w http.ResponseWriter, _ *http.Request) { - if err := pongTemplate.Execute(w, nil); err != nil { - log.Printf("Warning: pong: %s", err) +func preloadPong() []byte { + pongTemplate, err := template.New("pong").Parse(pongString) + if err != nil { + log.Fatalf("pong.html.template: invalid template: %s", err) + } + buf := new(bytes.Buffer) + if err := pongTemplate.Execute(buf, nil); err != nil { + log.Fatalf("pong.html.template: error: %s", err) } + return buf.Bytes() +} + +var pongHTML = preloadPong() + +func pong(w http.ResponseWriter, r *http.Request) { + http.ServeContent(w, r, "pong.html", serverStartTime, bytes.NewReader(pongHTML)) } //go:embed static @@ -76,6 +178,7 @@ func main() { flag.Parse() mux := http.NewServeMux() + mux.HandleFunc("/blog/", blog) mux.HandleFunc("/pong", pong) jellyfinURL, err := url.Parse("http://localhost:8096") if err != nil { diff --git a/templates/blog/post.html.template b/templates/blog/post.html.template new file mode 100644 index 0000000..ba5e81f --- /dev/null +++ b/templates/blog/post.html.template @@ -0,0 +1,4 @@ +
+ ⬅️Home +{{template "post"}} +
diff --git a/templates/blog/posts/2024-10-22_nix_flakes.html.template b/templates/blog/posts/2024-10-22_nix_flakes.html.template new file mode 100644 index 0000000..b9b2ca0 --- /dev/null +++ b/templates/blog/posts/2024-10-22_nix_flakes.html.template @@ -0,0 +1,82 @@ +

nix flakes for busy people

+ +

TL;DR

+ +

To enable flakes on a NixOS system

+ +

Just put

+ +
+  nix.settings.experimental-features = [
+    "nix-command"
+    "flakes"
+  ];
+
+ +

in your configuration.nix. IMO it's a shame that flakes +have been locked behind experimental for so long, it's a large part of +why they have a bad reputation. It's absolutely worth enabling them +since they're so useful.

+ +

To use a flake from github

+ +
+nix shell github:ggerganov/llama.cpp
+
+ +

or to install a specific flake output:

+ +
+nix shell github:ggerganov/llama.cpp#rocm
+
+ +

To list the outputs for a specific flake, you can usually type +nix shell github:ggerganov/llama.cpp#<tab> and your +shell may or may not list the outputs. If anyone has a better way to do this, +please let me know.

+ +

To install a flake in your system profile, use

+ +
+nix profile install github:ggerganov/llama.cpp
+
+ +

Here is the greatest power of nix flakes: the ability to install and +use them as first-class packages just like anything from nixpkgs. It +feels a lot better integrated than something like the AUR.

+ +

To create a nix flake for your own project

+ +
+nix flake init -t templates#go-hello
+
+ +

Replace go-hello with whatever template you want that most closely +matches your project configuration. Unfortunately many of these templates +have the questionable behavior of also overwriting source files in the +current directory, so I recommend only running nix flake init in an +empty directory, and copying the flake.nix file over to your +existing project.

+ +

A philosophical intro to nix flakes

+ +

Flakes are a way to replace the centralized nixpkgs repository with +decentralized packages controlled directly by upstream. In many ways +Linux packaging has been moving more and more towards upstream with +things like appimages or flatpak, and being able to remove many of the +distro-specific hacks needed to make things run on various systems. Nix +flakes address this same issue, by allowing upstream to control their own +flake.nix, and pull in the specific versions of any dependencies they +might need. They also give an easy way to install any programs that +might not be packaged yet for nixpkgs, as long as anyone has written a +flake and published it online.

+ +

The bad parts

+ +

Nix flakes are still rough around the edges, and the worst part for +a new Nix user is the lack of documentation. This is a large part of why +I'm writing yet another nix flakes tutorial! +https://xeiaso.net/blog/nix-flakes-1-2022-02-21 +is really good, but reading this as a new user, I only wanted to know +how to install an app packaged with flakes, and I didn't care so much +about how to package my own stuff yet.

diff --git a/templates/index.html.template b/templates/index.html.template index 49316cc..0d83073 100644 --- a/templates/index.html.template +++ b/templates/index.html.template @@ -1,21 +1,17 @@ - - - - rose.moe - - - - -
-

roseh.moe

-
+
+

roseh.moe

+
-
-
- A dragon loli -
+ - - +

Games? (Warning: loud)

+
+ +{{range .}} +
+

{{name .}}

+
+{{end}} diff --git a/templates/outline.html.template b/templates/outline.html.template new file mode 100644 index 0000000..960a880 --- /dev/null +++ b/templates/outline.html.template @@ -0,0 +1,11 @@ + + + + rose.moe + + + + +{{template "body" .}} + + -- cgit v1.3.1