From cfe496dcb71b86681202d26bfe270ee5cc5c92ac Mon Sep 17 00:00:00 2001 From: wanderer Date: Mon, 8 Aug 2022 15:20:50 +0200 Subject: [PATCH] embed homepage in a Go app (#33) 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. Co-authored-by: surtur Reviewed-on: https://git.dotya.ml/dotya.ml/homepage/pulls/33 --- .drone.yml | 88 +++++++++++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 7 ++++ Dockerfile | 39 +++++++++--------- docker-compose.yml | 4 +- go.mod | 3 ++ main.go | 32 +++++++++++++++ 6 files changed, 150 insertions(+), 23 deletions(-) create mode 100644 go.mod create mode 100644 main.go diff --git a/.drone.yml b/.drone.yml index c305df0..d6e1983 100644 --- a/.drone.yml +++ b/.drone.yml @@ -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: {} --- diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bfdfc50..3380df2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 ... diff --git a/Dockerfile b/Dockerfile index ff44537..4a11814 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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"] diff --git a/docker-compose.yml b/docker-compose.yml index ba34f08..f412030 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9db053b --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.dotya.ml/dotya.ml/homepage + +go 1.18 diff --git a/main.go b/main.go new file mode 100644 index 0000000..34ed110 --- /dev/null +++ b/main.go @@ -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) + } +}