embed homepage in a Go app
All checks were successful
continuous-integration/drone/pr Build is passing

the entire './public' folder that Hugo produces is embedded into a
variable of 'embed.FS' type and served directly using the default http
mux that Go std offers.

ci, pre-commit, Dockerfile and compose file have all been updated
accordingly.

nginx is no longer needed to front the site files, which enabled
switching to a SCRATCH image containing just a single statically linked
"homepage" app that has all files (html, css, js) embedded.
the containers are otherwise empty (as the name SCRATCH suggests), which
further decreases potential attack surface area.
This commit is contained in:
surtur 2022-08-08 14:54:30 +02:00
parent d0c61e4847
commit 573c9da829
Signed by: wanderer
GPG Key ID: 19CE1EC1D9E0486D
6 changed files with 150 additions and 23 deletions

@ -31,6 +31,13 @@ steps:
- uname -r
- hadolint --version
- name: golang
pull: always
image: docker.io/library/golang:1.18.5-alpine3.16
commands:
- uname -r
- go version
---
kind: pipeline
type: docker
@ -47,6 +54,9 @@ trigger:
depends_on:
- pull
environment:
CGO_ENABLED: 0
steps:
- name: hugo-extended
pull: if-not-exists
@ -57,6 +67,44 @@ steps:
- hugo version
- hugo --gc=true --minify
- name: go fmt
image: docker.io/library/golang:1.18.5-alpine3.16
volumes:
- name: gopath
path: /go
depends_on:
- clone
commands:
- go fmt
- name: go vet
image: docker.io/library/golang:1.18.5-alpine3.16
volumes:
- name: gopath
path: /go
depends_on:
- go fmt
commands:
- go vet
- name: go build
pull: if-not-exists
image: docker.io/library/golang:1.18.5-alpine3.16
volumes:
- name: gopath
path: /go
depends_on:
- go vet
# wait until the site is output into './public'.
- hugo-extended
commands:
- go build -v -ldflags "-s -w -X main.Version=${DRONE_COMMIT}" .
volumes:
- name: gopath
temp: {}
---
kind: pipeline
type: docker
@ -178,6 +226,9 @@ node:
depends_on:
- build
environment:
CGO_ENABLED: 0
steps:
- name: hugo-extended
pull: if-not-exists
@ -204,12 +255,47 @@ steps:
- hadolint --version
- hadolint Dockerfile
- name: go fmt
image: docker.io/library/golang:1.18.5-alpine3.16
volumes:
- name: gopath
path: /go
depends_on:
- clone
commands:
- go fmt
- name: go vet
image: docker.io/library/golang:1.18.5-alpine3.16
volumes:
- name: gopath
path: /go
depends_on:
- go fmt
commands:
- go vet
- name: go build
image: docker.io/library/golang:1.18.5-alpine3.16
volumes:
- name: gopath
path: /go
depends_on:
- go vet
# wait until the site is output into './public'.
- hugo-extended
commands:
- go build -v -ldflags "-s -w -X main.Version=${DRONE_COMMIT}" .
- name: build
pull: always
image: tmaier/docker-compose:latest
depends_on:
- rm-intermediate
- hadolint
- go fmt
- go vet
- go build
volumes:
- name: s
path: /var/run/docker.sock
@ -237,6 +323,8 @@ volumes:
- name: s
host:
path: /var/run/docker.sock
- name: gopath
temp: {}
---

@ -19,4 +19,11 @@ repos:
language: system
entry: yamllint .
pass_filenames: false
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.5.0
hooks:
- id: go-mod-tidy
- id: go-unit-tests
- id: golangci-lint
- id: go-build
...

@ -1,28 +1,25 @@
FROM immawanderer/fedora-hugo:linux-amd64 AS hugobuild
RUN mkdir -pv /homepage
COPY . /homepage
FROM docker.io/immawanderer/fedora-hugo:linux-amd64 AS hugobuild
WORKDIR /homepage
COPY . .
RUN git submodule init \
&& git submodule update --recursive \
&& hugo version
# "DL3059 info: Multiple consecutive `RUN` instructions.
# Consider consolidation."
# hadolint ignore=DL3059
RUN hugo --minify --gc=true
&& git submodule update --recursive \
&& hugo version \
&& hugo --minify --gc=true --cleanDestinationDir
WORKDIR /
FROM docker.io/library/golang:1.18.5-alpine3.16 AS gobuild
COPY --from=hugobuild /homepage/ /homepage/
FROM nginx:mainline-alpine
COPY --from=hugobuild /homepage/public/ /usr/share/nginx/html
WORKDIR /homepage
# tripple slash reference
# https://stackoverflow.com/questions/5190966/using-sed-to-insert-tabs/5191165#5191165
RUN sed -i -e 's/^worker_processes auto;/worker_processes auto;/'\
-e "/^events {$/ a \\\tmulti_accept on;\n\tuse epoll;"\
-e "/^http {$/ a \\\tserver_tokens off;\n\tetag off;\n"\
-e 's/#tcp_nopush/tcp_nopush/'\
-e "/tcp_nopush/ a \\\ttcp_nodelay on;\n\terror_page 404 /404.html;"\
-e "s/^ */$(printf '\t')/"\
/etc/nginx/nginx.conf
ARG VCS_REF=development
RUN CGO_ENABLED=0 GOFLAGS='-trimpath -mod=readonly -modcacherw' \
go build -o homepage-app -v -ldflags "-s -w -X main.version=$VCS_REF" .
FROM scratch
COPY --from=gobuild /homepage/homepage-app /homepage
ENTRYPOINT ["/homepage"]

@ -10,7 +10,7 @@ services:
- internal-nw
- default
ports:
- 127.0.0.1:1314:80
- 127.0.0.1:1314:1314
restart: always
volumes:
# So that traefik can listen to the Docker events
@ -26,7 +26,7 @@ services:
restart: always
labels:
- traefik.enable=true
- traefik.http.services.homepage.loadbalancer.server.port=80
- traefik.http.services.homepage.loadbalancer.server.port=1314
- traefik.http.routers.homepage.rule=Host(`localhost`) || Host(`127.0.0.1`) || Host(`homepage`) || Host(`6426tqrh4y5uobmo5y2csaip3m3avmjegd2kpa24sadekpxglbm34aqd.onion`)
# ref: https://stackoverflow.com/a/61976953
# ref: https://github.com/traefik/traefik/issues/563

3
go.mod Normal file

@ -0,0 +1,3 @@
module git.dotya.ml/dotya.ml/homepage
go 1.18

32
main.go Normal file

@ -0,0 +1,32 @@
package main
import (
"embed"
"io/fs"
"log"
"net/http"
)
var version = "development"
//go:embed public/*
var embeddedPublic embed.FS
func main() {
root, err := fs.Sub(embeddedPublic, "public")
if err != nil {
log.Fatal(err)
}
fs := http.FileServer(http.FS(root))
http.Handle("/", fs)
log.Printf("app built from revision '%s'\n", version)
log.Print("Listening on :1314...")
err = http.ListenAndServe(":1314", nil)
if err != nil {
log.Fatal(err)
}
}