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 - uname -r
- hadolint --version - hadolint --version
- name: golang
pull: always
image: docker.io/library/golang:1.18.5-alpine3.16
commands:
- uname -r
- go version
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
@ -47,6 +54,9 @@ trigger:
depends_on: depends_on:
- pull - pull
environment:
CGO_ENABLED: 0
steps: steps:
- name: hugo-extended - name: hugo-extended
pull: if-not-exists pull: if-not-exists
@ -57,6 +67,44 @@ steps:
- hugo version - hugo version
- hugo --gc=true --minify - 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 kind: pipeline
type: docker type: docker
@ -178,6 +226,9 @@ node:
depends_on: depends_on:
- build - build
environment:
CGO_ENABLED: 0
steps: steps:
- name: hugo-extended - name: hugo-extended
pull: if-not-exists pull: if-not-exists
@ -204,12 +255,47 @@ steps:
- hadolint --version - hadolint --version
- hadolint Dockerfile - 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 - name: build
pull: always pull: always
image: tmaier/docker-compose:latest image: tmaier/docker-compose:latest
depends_on: depends_on:
- rm-intermediate - rm-intermediate
- hadolint - hadolint
- go fmt
- go vet
- go build
volumes: volumes:
- name: s - name: s
path: /var/run/docker.sock path: /var/run/docker.sock
@ -237,6 +323,8 @@ volumes:
- name: s - name: s
host: host:
path: /var/run/docker.sock path: /var/run/docker.sock
- name: gopath
temp: {}
--- ---

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

@ -10,7 +10,7 @@ services:
- internal-nw - internal-nw
- default - default
ports: ports:
- 127.0.0.1:1314:80 - 127.0.0.1:1314:1314
restart: always restart: always
volumes: volumes:
# So that traefik can listen to the Docker events # So that traefik can listen to the Docker events
@ -26,7 +26,7 @@ services:
restart: always restart: always
labels: labels:
- traefik.enable=true - 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`) - 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://stackoverflow.com/a/61976953
# ref: https://github.com/traefik/traefik/issues/563 # 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)
}
}