The tech behind this site

I believe in simplicity. Simplicity makes things easy to reason about. This site is an example on making things as simple as possible. It does not make use of any complex frameworks and the source code dependencies are minimal.

In essense it is a static site. A bunch of html files served along with one css file for styling. However i do not hand code these html files since i have got better things to do. But instead of finding the first static website generator on duckduckgo and spend my time learning it’s template syntax, i decided to make my own static site generator. It is written in golang as that is my goto programming language at the moment.

The generator takes a bunch of markdown files and turn them in to basic html. Each markdown file is wrapped in a simple template. This generator/dev server is currently taking up 164 lines of code and makes use of one external dependency. A markdown generator. That’s all.

Deployment

Deployment is handled by using sr.ht’s build service. Since this is a simple static site, the deployment too is simple. It consists of two steps, first generate the static html and then upload these html files to the server using scp.


Code main.go

//go:generate go run cozylabs.eu/www --generate

package main

import (
	"flag"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"sort"
	"strings"

	"github.com/russross/blackfriday"
)

var tmpl = `
<html lang="en">
	<head>
		<meta charset="utf-8">

		<title>Cozylabs</title>
		<meta name="description" content="Cozylabs">
		<link rel="stylesheet" type="text/css" href="/hack.css">
	</head>
	<body class="hack">
		{{.}}
		<hr/>
		<div style="text-align:center;">*</div>
	</body>
</html>
`

func main() {
	gen := flag.Bool("generate", false, "generate static html")
	flag.Parse()

	if *gen {
		os.MkdirAll("dist", 0700)
		log.Println("Generating static pages")
		dir, err := os.Open(".")
		if err != nil {
			panic(err)
		}
		processFolder(dir, "")
		copyFile("hack.css", "dist/hack.css")
		return
	}

	port := "localhost:10000"
	log.Printf("Serving on %v", port)
	if err := http.ListenAndServe(port, &Server{fs: http.FileServer(http.Dir("."))}); err != nil {
		panic(err)
	}
}

func processFolder(dir *os.File, basePath string) {
	log.Printf("Processing %v %v", basePath, dir.Name())
	files, err := dir.Readdir(-1)
	if err != nil {
		panic(err)
	}
	for _, f := range files {
		fullPath := path.Join(basePath, f.Name())
		file, err := os.Open(fullPath)
		if err != nil {
			panic(err)
		}
		if f.IsDir() {
			processFolder(file, fullPath)
			file.Close()
			continue
		}

		if strings.HasSuffix(f.Name(), ".md") {
			os.MkdirAll(path.Join("dist", basePath), 0700)
			// generate markdown and write file...
			fc, err := ioutil.ReadAll(file)
			if err != nil {
				panic(err)
			}
			res := blackfriday.Run(fc)
			if err := ioutil.WriteFile(path.Join("dist", strings.Replace(file.Name(), ".md", ".html", 1)), res, 0600); err != nil {
				panic(err)
			}
		}
		file.Close()
	}
}

func copyFile(file string, path string) {
	f, err := os.Open(file)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	nf, err := os.Create(path)
	if err != nil {
		panic(err)
	}
	defer nf.Close()
	if _, err := io.Copy(nf, f); err != nil {
		panic(err)
	}
}

type Server struct {
	fs http.Handler
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/blog" {
		// Render list...
		// List all markdown files in blog folder...
		dir, err := os.Open("blog")
		if err != nil {
			panic(err)
		}
		files, err := dir.Readdirnames(-1)
		if err != nil {
			panic(err)
		}
		sort.Strings(files)

		links := []string{}
		for _, f := range files {
			// If markdown, then
			if strings.HasSuffix(f, ".md") {
				links = append(links, fmt.Sprintf("/blog/%v", f))
			}
		}
		for _, l := range links {
			w.Write([]byte(fmt.Sprintf("<a href=\"%v\">%v</a>", l, l)))
		}
		return
	}

	if r.URL.Path == "/" {
		r.URL.Path = "/index.md"
	}

	if strings.HasSuffix(r.URL.Path, ".md") {
		path := strings.TrimLeft(r.URL.Path, "/")
		file, err := ioutil.ReadFile(path)
		if err != nil {
			panic(err)
		}

		t, err := template.New("tmpl").Parse(tmpl)
		if err != nil {
			panic(err)
		}
		t.Execute(w, template.HTML(blackfriday.Run(file)))
		return
	}

	s.fs.ServeHTTP(w, r)
}

Deployment script deploy.yml

image: archlinux
packages:
  - go
secrets:
  - fe828eb9-c0f8-468e-87dd-9b66e03c76c4
sources:
  - git@git.sr.ht:~jzs/cozylabs-www
tasks:
  - deploy: |
      cd cozylabs-www
      go generate
      scp -o StrictHostKeyChecking=no -r dist/* deployer@cozylabs.eu:/var/www/cozylabs.eu

twitter github sourcehut