docs: Hugo on Cloudflare pages
All checks were successful
Deploy pages / deploy (push) Successful in 37s
All checks were successful
Deploy pages / deploy (push) Successful in 37s
This commit is contained in:
parent
5e2b34767f
commit
852f718af7
3 changed files with 156 additions and 1 deletions
|
@ -10,7 +10,7 @@ tags = [
|
|||
]
|
||||
+++
|
||||
|
||||
This is a follow up post to ["Joining libvirt {{<abbr short="VM" full="Virtual Machine" >}}s and Podman container in a common network"]({{<relref "libvirt-podman-network-mesh.md" >}}).
|
||||
This is a follow up post to ["Joining libvirt {{<abbr short="VM" full="Virtual Machine" >}}s and Podman container in a common network"]({{<relref "2022-02-libvirt-podman-network-mesh.md" >}}).
|
||||
Therefore I won't cover all the basics again and how to configure libvirt because nothing's changed on that side.
|
||||
|
||||
## Podman 4.0
|
155
content/post/2024-04-forgejo-deploy-hugo.md
Normal file
155
content/post/2024-04-forgejo-deploy-hugo.md
Normal file
|
@ -0,0 +1,155 @@
|
|||
+++
|
||||
author = "Peter Kurfer"
|
||||
title = "Build & deploy a Hugo site with Gitea/Forgejo actions"
|
||||
description = "How to host a Hugo site with Cloudflare pages and deploy it automatically with Forgejo actions"
|
||||
date = "2024-04-30"
|
||||
tags = [
|
||||
"hugo",
|
||||
"cloudflare",
|
||||
"CD/CD",
|
||||
"actions"
|
||||
]
|
||||
+++
|
||||
|
||||
I admit it. I like self-hosting.
|
||||
I like the idea of being able to control every aspect of my infrastructure.
|
||||
It was only consequent to also self-host my blog.
|
||||
This article describes my odyssey and why I ended up letting [Cloudflare](https://www.cloudflare.com/) do the hosting.
|
||||
|
||||
In the beginning - there was a repository.
|
||||
As we all know, the repository is the truth.
|
||||
When the time came for deploying the blog, I already had a Kubernetes (K8s) cluster at hand so the obvious choice was to containerize the web page and host it there.
|
||||
I wrote a simple Dockerfile with a multi-stage build, just like this:
|
||||
|
||||
```Dockerfile
|
||||
FROM docker.io/golang:1-alpine as builder
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
RUN apk add -U --no-cache hugo git
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
COPY . /src/
|
||||
|
||||
RUN hugo --minify --environment production --config config.toml
|
||||
|
||||
FROM caddy as runtime
|
||||
|
||||
COPY --from=builder /src/public /usr/share/caddy
|
||||
```
|
||||
|
||||
prepared my deployment manifests and setup a CI pipeline (back then with DroneCI) to deploy everything.
|
||||
|
||||
So far so good, the only complicacy was that I now had two 'truths'.
|
||||
One was the repository and the second one was the container registry - let alone that I also had to 💸 the storage for both.
|
||||
Of course, various container registries have cleanup options but being a software engineer, why using something existing when you can build the 11th solution to solve the same problem, right?
|
||||
|
||||
Yes...actually, no!
|
||||
|
||||
In the beginning I just accepted the fact and went on.
|
||||
Every now and then, when the amount of images became costlier, I manually deleted a few until I reached a reasonable count - say...five, I mean in the end there was no reason to keep any old version at all, but you know, I was lazy.
|
||||
At some point I had a similar problem at work with our SPAs and I couldn't help but wonder: is this really the best way?
|
||||
Not only because I'm duplicating the content every time, but also the web server needs patching, every now and then a breaking change in the configuration system happens and so on and so forth.
|
||||
I came across the possibility to serve a S3 bucket (or similar) directly from a [K8s ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/#resource-backend).
|
||||
That sounded awesome!
|
||||
No need to build a container image, no need to waste compute resources, simple copy to S3 bucket and be done with it!
|
||||
|
||||
So I came back to my blog and tried to migrate to this approach.
|
||||
I wasted a few hours of my spare time, only realizing that - apparently - Cloudflare R2 or some CLI or something else is ignoring the content type of my files, leaving me with `application/octet-stream` which is absolutely useless for web pages.
|
||||
It might be different when I would use [MinIO](https://min.io/) or AWS S3 but I didn't want to waste even more resources (and 💵) on hosting a MinIO instance in my cluster.
|
||||
Also, I am already using Hetzner Cloud and didn't feel like distributing my costs around multiple cloud providers, so I started looking for alternative solutions.
|
||||
|
||||
I then stumbled upon [Cloudflare Pages](https://pages.cloudflare.com/).
|
||||
After a 'quick' prototype I was happy and decided to migrate - actually not so quick, I spent a few evenings on migrating my whole DNS setup to [external-dns](https://github.com/kubernetes-sigs/external-dns) and experimented with Cloudflare DNS for DoS protection but that's a topic for another day.
|
||||
|
||||
The only other problem I had was: I also got rid of DroneCI in favor of [Forgejo Actions](https://forgejo.org/docs/latest/user/actions/).
|
||||
I know, if I would use GitHub, there would be perfect integration from Cloudflare to build my Hugo page and deploy it, but we don't want to make things too easy, right?
|
||||
|
||||
But using Forgejo Actions also seemed pretty straight forward:
|
||||
|
||||
```yaml
|
||||
name: Deploy pages
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v3
|
||||
- name: Build
|
||||
run: hugo --minify --environment production
|
||||
- name: Deploy
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_PAGES_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: pages deploy public --project-name=blog
|
||||
```
|
||||
|
||||
Well, not so fast kiddo!
|
||||
|
||||
At first I noticed, when using [Hugo modules](https://gohugo.io/hugo-modules/) you need to fetch those modules before being able to build anything, alright:
|
||||
|
||||
```yaml
|
||||
// ...
|
||||
- name: Build
|
||||
run: |
|
||||
hugo mod get
|
||||
hugo --minify --environment production
|
||||
// ...
|
||||
```
|
||||
|
||||
then, obviously, I realized, for being able to fetch those modules, you need a Go SDK, there you go (pun intended):
|
||||
|
||||
```yaml
|
||||
// ...
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.22.x"
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v3
|
||||
// ...
|
||||
```
|
||||
|
||||
and now we're getting - finally - to the point when things got really annoying...
|
||||
I'm using the [github.com/LordMathis/hugo-theme-nightfall](https://github.com/LordMathis/hugo-theme-nightfall) theme.
|
||||
Although being a very minimalistic theme, it requires [dart-sass](https://gohugo.io/functions/resources/tocss/#dart-sass).
|
||||
Even though this also seem straight forward, especially because there's only [documentation for Github Actions](https://gohugo.io/functions/resources/tocss/#github-pages), with Forgejo Actions it isn't.
|
||||
The key difference between Github Actions and Forgejo Actions is, that Forgejo Actions are running in containers.
|
||||
The officially recommended way to install `dart-sass` in Github Actions is via `snap`, but snap doesn't really work in containers, so I had find another way.
|
||||
When doing some research, you might come across the official [`dart-sass` repository](https://github.com/sass/dart-sass) that mentions another installation method:
|
||||
|
||||
```bash
|
||||
npm install -g sass
|
||||
```
|
||||
|
||||
but:
|
||||
|
||||
> The `--embedded` command-line flag is not available when you install Dart Sass as an npm package.
|
||||
|
||||
(*see [here](https://github.com/sass/dart-sass?tab=readme-ov-file#embedded-dart-sass)*)
|
||||
|
||||
unfortunately, Hugo requires the `--embedded` flag, so also not an option.
|
||||
Eventually I came around this abomination:
|
||||
|
||||
```yaml
|
||||
- name: Install sass
|
||||
run: |
|
||||
export SASS_VERSION=$(curl https://api.github.com/repos/sass/dart-sass/releases | jq -r '. | first |.tag_name | capture("(?<version>[[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+)") | .version')
|
||||
curl -L "https://github.com/sass/dart-sass/releases/download/${SASS_VERSION}/dart-sass-${SASS_VERSION}-linux-arm64.tar.gz" | tar xvz -C /opt/
|
||||
ln -s /opt/dart-sass/sass /usr/local/bin/
|
||||
```
|
||||
|
||||
*Don't get confused by the huge capture in the `jq` expression, I'm using this snippet whenever I have to use the version of a package in the filename and this way I don't have to think about, is there a `v` prefix or not, looking at you 'goreleaser' 👀*
|
||||
|
||||
That downloads the latest release of `dart-sass` and makes it available in the `$PATH`.
|
||||
So far I'm not considering the CPU architecture because whenever possible I'm running my CI jobs on ARM machines anyway, but if I find the time, I might try to implement a custom action similar to `peaceiris/actions-hugo@v3` but with `dart-sass` support.
|
||||
|
||||
You can imagine how happy I was realizing the `cloudflare/wrangler-action@v3` step 'just worked' ™.
|
Loading…
Reference in a new issue