Hugo Blogging with Contentful and Azure Static Sites

Published: Feb 9, 2023 by Isaac Johnson

Following our last few posts about CMS, I wanted to dig into Hugo a bit more. Hugo is an Open Source static site generator that follows similar systems like Jekyll (used here) and Octopress.

Today we will install Hugo and connect it to a CMS like Contentful. We’ll then generate a local static site before tackling a Github Workflow to build it in CI. We’ll jump into Azure Static Web Apps to show how we can use the free tier to host the blog in Azure. We’ll wrap up by tying in a custom domain from Azure DNS and using Github “Dispatch” triggers to trigger CICD directly from Contentful webhooks.

History and Origins

/content/images/2023/02/isaacjohnson_A_soft_light_blue_gopher_with_buck_teeth_wearing_a_1bde6c21-9ce8-45af-ac10-4bc895604185.png

It was created in 2013 by Steve Francia (spf13). He wanted to create a fast flexible site generator with themes and plugins written in Go.

In 2016, Steve released to it to be maintained by OS volunteers as it continues to evolve and grow. *In looking up Steve, I found he even has his own VIM Distribution. If you look up his profile he has had leadership roles in 5 of the top 100 OS projects including Drupal, MongoDB, Hugo, Docker and Go).

Install Hugo

builder@DESKTOP-72D2D9T:~/Workspaces$ mkdir hugoBlog
builder@DESKTOP-72D2D9T:~/Workspaces$ cd hugoBlog/
builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog$ sudo apt update && sudo apt install -y hugo
[sudo] password for builder:
Hit:1 http://archive.ubuntu.com/ubuntu focal InRelease
Get:2 https://apt.releases.hashicorp.com focal InRelease [17.1 kB]
Get:3 http://archive.ubuntu.com/ubuntu focal-updates InRelease [114 kB]
Get:4 http://security.ubuntu.com/ubuntu focal-security InRelease [114 kB]
Get:5 https://packages.microsoft.com/repos/azure-cli focal InRelease [3029 B]
Get:6 https://packages.cloud.google.com/apt cloud-sdk InRelease [6361 B]
Get:7 https://packages.microsoft.com/repos/code stable InRelease [3023 B]
Get:8 http://archive.ubuntu.com/ubuntu focal-backports InRelease [108 kB]
Err:2 https://apt.releases.hashicorp.com focal InRelease
  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY AA16FCBCA621E701
Get:10 https://packages.microsoft.com/repos/azure-cli focal/main all Packages [4043 B]
Err:6 https://packages.cloud.google.com/apt cloud-sdk InRelease
  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY B53DC80D13EDEF05
Get:11 https://packages.microsoft.com/repos/code stable/main amd64 Packages [58.5 kB]
Get:12 https://packages.microsoft.com/repos/code stable/main armhf Packages [59.1 kB]
Get:13 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages [2345 kB]
Get:14 https://packages.microsoft.com/repos/code stable/main arm64 Packages [58.9 kB]
Get:15 http://archive.ubuntu.com/ubuntu focal-updates/main Translation-en [405 kB]
Get:16 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 c-n-f Metadata [16.3 kB]
Get:17 http://archive.ubuntu.com/ubuntu focal-updates/restricted amd64 Packages [1564 kB]
Get:18 http://archive.ubuntu.com/ubuntu focal-updates/restricted Translation-en [221 kB]
Get:19 http://archive.ubuntu.com/ubuntu focal-updates/restricted amd64 c-n-f Metadata [620 B]
Get:20 http://archive.ubuntu.com/ubuntu focal-updates/universe amd64 Packages [1021 kB]
Get:21 http://archive.ubuntu.com/ubuntu focal-updates/universe Translation-en [237 kB]
Get:22 http://archive.ubuntu.com/ubuntu focal-updates/universe amd64 c-n-f Metadata [23.5 kB]
Get:23 http://archive.ubuntu.com/ubuntu focal-backports/universe amd64 Packages [24.9 kB]
Get:24 http://archive.ubuntu.com/ubuntu focal-backports/universe amd64 c-n-f Metadata [880 B]
Get:25 http://security.ubuntu.com/ubuntu focal-security/main amd64 Packages [1968 kB]
Get:9 https://download.opensuse.org/repositories/devel:kubic:libcontainers:unstable/Debian_Testing  InRelease [1275 B]
Err:9 https://download.opensuse.org/repositories/devel:kubic:libcontainers:unstable/Debian_Testing  InRelease
  The following signatures were invalid: EXPKEYSIG 4D64390375060AA4 devel:kubic OBS Project <devel:kubic@build.opensuse.org>
Get:26 http://security.ubuntu.com/ubuntu focal-security/main Translation-en [322 kB]
Get:27 http://security.ubuntu.com/ubuntu focal-security/main amd64 c-n-f Metadata [12.0 kB]
Get:28 http://security.ubuntu.com/ubuntu focal-security/restricted amd64 Packages [1467 kB]
Get:29 http://security.ubuntu.com/ubuntu focal-security/restricted Translation-en [207 kB]
.opensuse.org/repositories/devel:kubic:libcontainers:unstable/Debian_Testing  InRelease: The following signatures were invalid: EXPKEYSIG 4D64390375060AA4 devel:kubic OBS Project <devel:kubic@build.opensuse.org>
W: Failed to fetch https://apt.releases.hashicorp.com/dists/focal/InRelease  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY AA16FCBCA621E701
W: Failed to fetch https://download.opensuse.org/repositories/devel:kubic:libcontainers:unstable/Debian_Testing/InRelease  The following signatures were invalid: EXPKEYSIG 4D64390375060AA4 devel:kubic OBS Project <devel:kubic@build.opensuse.org>
W: Failed to fetch https://packages.cloud.google.com/apt/dists/cloud-sdk/InRelease  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY B53DC80D13EDEF05
W: Some index files failed to download. They have been ignored, or old ones used instead.
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages were automatically installed and are no longer required:
  libfwupdplugin1 libgbm1 libjs-sphinxdoc libjs-underscore libsecret-1-0 libsecret-common libwayland-server0 libxmlb1 python-pkginfo-doc python3-adal
  python3-aiohttp python3-applicationinsights python3-argcomplete python3-async-timeout python3-azext-devops python3-azure python3-azure-cli
  python3-azure-cli-core python3-azure-cli-telemetry python3-azure-cosmos python3-azure-cosmosdb-table python3-azure-datalake-store
  python3-azure-functions-devops-build python3-azure-multiapi-storage python3-azure-storage python3-bcrypt python3-cffi python3-fabric
  python3-humanfriendly python3-invoke python3-isodate python3-javaproperties python3-jsmin python3-jsondiff python3-knack python3-mock python3-msal
  python3-msal-extensions python3-msrest python3-msrestazure python3-multidict python3-paramiko python3-pbr python3-pkginfo python3-ply
  python3-portalocker python3-psutil python3-pycparser python3-requests-oauthlib python3-scp python3-sshtunnel python3-tabulate python3-tz python3-uamqp
  python3-vsts-cd-manager python3-websocket python3-xmltodict python3-yarl
Use 'sudo apt autoremove' to remove them.
The following additional packages will be installed:
  libsass1
The following NEW packages will be installed:
  hugo libsass1
0 upgraded, 2 newly installed, 0 to remove and 103 not upgraded.
Need to get 10.6 MB of archives.
After this operation, 47.5 MB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu focal/universe amd64 libsass1 amd64 3.6.3-1ubuntu1 [684 kB]
Get:2 http://archive.ubuntu.com/ubuntu focal/universe amd64 hugo amd64 0.68.3-1 [9893 kB]
Fetched 10.6 MB in 1s (8740 kB/s)
Selecting previously unselected package libsass1:amd64.
(Reading database ... 191611 files and directories currently installed.)
Preparing to unpack .../libsass1_3.6.3-1ubuntu1_amd64.deb ...
Unpacking libsass1:amd64 (3.6.3-1ubuntu1) ...
Selecting previously unselected package hugo.
Preparing to unpack .../hugo_0.68.3-1_amd64.deb ...
Unpacking hugo (0.68.3-1) ...
Setting up libsass1:amd64 (3.6.3-1ubuntu1) ...
Setting up hugo (0.68.3-1) ...
Processing triggers for man-db (2.9.1-1) ...
Processing triggers for libc-bin (2.31-0ubuntu9.9) ...
/sbin/ldconfig.real: /usr/lib/wsl/lib/libcuda.so.1 is not a symbolic link

We now create a new Hugo site

builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog$ hugo new site myBlog
Congratulations! Your new Hugo site is created in /home/builder/Workspaces/hugoBlog/myBlog.

Just a few more steps and you're ready to go:

1. Download a theme into the same-named folder.
   Choose a theme from https://themes.gohugo.io/ or
   create your own with the "hugo new theme <THEMENAME>" command.
2. Perhaps you want to add some content. You can add single files
   with "hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>".
3. Start the built-in live server via "hugo server".

Visit https://gohugo.io/ for quickstart guide and full documentation.

Then go in there and add a theme

builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog$ cd myBlog/
builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ git init
Initialized empty Git repository in /home/builder/Workspaces/hugoBlog/myBlog/.git/
builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ git checkout -b main
Switched to a new branch 'main'
builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ git submodule add https://github.com/panr/hugo-theme-terminal themes/terminal
Cloning into '/home/builder/Workspaces/hugoBlog/myBlog/themes/terminal'...
remote: Enumerating objects: 2796, done.
remote: Counting objects: 100% (257/257), done.
remote: Compressing objects: 100% (159/159), done.
remote: Total 2796 (delta 125), reused 199 (delta 93), pack-reused 2539
Receiving objects: 100% (2796/2796), 3.51 MiB | 5.85 MiB/s, done.
Resolving deltas: 100% (1578/1578), done.
builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ echo "theme = 'terminal'" >> config.toml

I found the apt install hugo is too old

builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ hugo server
Building sites … ERROR 2023/02/04 17:54:43 render of "home" failed: execute of template failed: template: _default/index.html:22:7: executing "footer" at <partial "footer.html" .>: error calling partial: "/home/builder/Workspaces/hugoBlog/myBlog/themes/terminal/layouts/partials/footer.html:15:41": execute of template failed: template: partials/footer.html:15:41: executing "partials/footer.html" at <js>: can't evaluate field Build in type string
ERROR 2023/02/04 17:54:43 render of "taxonomyTerm" failed: execute of template failed: template: _default/terms.html:22:7: executing "footer" at <partial "footer.html" .>: error calling partial: "/home/builder/Workspaces/hugoBlog/myBlog/themes/terminal/layouts/partials/footer.html:15:41": execute of template failed: template: partials/footer.html:15:41: executing "partials/footer.html" at <js>: can't evaluate field Build in type string
Built in 85 ms
Error: Error building site: failed to render pages: render of "taxonomyTerm" failed: execute of template failed: template: _default/terms.html:22:7: executing "footer" at <partial "footer.html" .>: error calling partial: "/home/builder/Workspaces/hugoBlog/myBlog/themes/terminal/layouts/partials/footer.html:15:41": execute of template failed: template: partials/footer.html:15:41: executing "partials/footer.html" at <js>: can't evaluate field Build in type string
builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ hugo version
Hugo Static Site Generator v0.68.3/extended linux/amd64 BuildDate: 2020-03-25T06:15:45Z

I did an apt remove and install with homebrew instead

builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ sudo apt remove hugo
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages were automatically installed and are no longer required:
  libfwupdplugin1 libgbm1 libjs-sphinxdoc libjs-underscore libsass1 libsecret-1-0 libsecret-common libwayland-server0 libxmlb1 python-pkginfo-doc
  python3-adal python3-aiohttp python3-applicationinsights python3-argcomplete python3-async-timeout python3-azext-devops python3-azure python3-azure-cli
  python3-azure-cli-core python3-azure-cli-telemetry python3-azure-cosmos python3-azure-cosmosdb-table python3-azure-datalake-store
  python3-azure-functions-devops-build python3-azure-multiapi-storage python3-azure-storage python3-bcrypt python3-cffi python3-fabric
  python3-humanfriendly python3-invoke python3-isodate python3-javaproperties python3-jsmin python3-jsondiff python3-knack python3-mock python3-msal
  python3-msal-extensions python3-msrest python3-msrestazure python3-multidict python3-paramiko python3-pbr python3-pkginfo python3-ply
  python3-portalocker python3-psutil python3-pycparser python3-requests-oauthlib python3-scp python3-sshtunnel python3-tabulate python3-tz python3-uamqp
  python3-vsts-cd-manager python3-websocket python3-xmltodict python3-yarl
Use 'sudo apt autoremove' to remove them.
The following packages will be REMOVED:
  hugo
0 upgraded, 0 newly installed, 1 to remove and 103 not upgraded.
After this operation, 44.7 MB disk space will be freed.
Do you want to continue? [Y/n] y
(Reading database ... 191657 files and directories currently installed.)
Removing hugo (0.68.3-1) ...
Processing triggers for man-db (2.9.1-1) ...
builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ brew install hugo
Running `brew update --auto-update`...

==> Auto-updated Homebrew!
Updated 4 taps (codefresh-io/cli, homebrew/core, knative/client and knative-sandbox/kn-plugins).
==> New Formulae
akku                                                grayskull                                           php@8.1
ancient                                             hermit                                              pipdeptree
ansible@6                                           httm                                                plz-cli
aptos                                               ibazel                                              podsync
aribb24                                             jbang                                               portablegl
aws-sam-cli                                         joker                                               protolint
bindgen                                             jreleaser                                           prowler
bossa                                               jscpd                                               prs
brpc                                                juicefs                                             psysh
buf                                                 keploy                                              qdmr
busted                                              knative-sandbox/kn-plugins/admin@1.8                quartz-wm
bzip3                                               knative-sandbox/kn-plugins/event@1.8                retdec
cargo-about                                         knative-sandbox/kn-plugins/func@1.8                 rnr
cargo-deny                                          knative-sandbox/kn-plugins/quickstart@1.8           ruby@3.1
cargo-make                                          knative-sandbox/kn-plugins/source-kafka@1.8         ruff
cargo-release                                       knative-sandbox/kn-plugins/source-kamelet@1.8       sapling
cascadia                                            knative/client/kn@1.8                               scriptisto
cdebug                                              kubefirst                                           secp256k1
cdsclient                                           kubent                                              seven-kingdoms
check-jsonschema                                    kubevious                                           simdutf
clang-build-analyzer                                kustomizer                                          skaffold@1.39
clitest                                             kwctl                                               skeema
cloudflare-wrangler2                                kwok                                                snakefmt
clusterawsadm                                       lemmeknow                                           sniffnet
cntb                                                libaribcaption                                      socket_vmnet
cocogitto                                           libemf2svg                                          souffle
code-cli                                            libfyaml                                            spectral-cli
cog                                                 libisofs                                            speedbump
conda-zsh-completion                                liblerc                                             sql-language-server
copa                                                libsais                                             standard
corrosion                                           licensed                                            steampipe
countdown                                           llama                                               stepci
cpuid                                               luacheck                                            stuffbin
crfsuite                                            m1ddc                                               stylelint
d2                                                  macpine                                             syft
dotnet@6                                            mariadb@10.9                                        syslog-ng
dstack                                              marksman                                            temporal
dufs                                                mdless                                              tetra
ecoji                                               metals                                              totp-cli
emqx                                                mimirtool                                           tproxy
enpass-cli                                          mpfrcx                                              tut
envd                                                naga-cli                                            twm
erigon                                              nap                                                 txt2man
evtx                                                nemu                                                unxip
fgbio                                               oauth2c                                             video-compare
flagd                                               ocm                                                 waybackpy
gf                                                  okta-awscli                                         wikibase-cli
ghc@9.2                                             opencl-clhpp-headers                                xcdiff
go-task                                             openvino                                            xinit
gobackup                                            osv-scanner                                         xmodmap
gokey                                               pandemics                                           xorg-server
grammarly-languageserver                            pari-nflistdata                                     xrdb
grantlee                                            pbzx                                                zf
graphqxl                                            pdf-diff                                            zsh-autopair

You have 12 outdated formulae installed.
You can upgrade them with brew upgrade
or list them with brew outdated.

==> Fetching dependencies for hugo: linux-headers@5.15, mpfr, libmpc, xz, zlib and binutils
==> Fetching linux-headers@5.15
==> Downloading https://ghcr.io/v2/homebrew/core/linux-headers/5.15/manifests/5.15.91
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/linux-headers/5.15/blobs/sha256:29e9a9b48e71ea0bbb43b1ea6290b97d57a433fe0617f918e0d80b7bbe188260
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:29e9a9b48e71ea0bbb43b1ea6290b97d57a433fe0617f918e0d80b7bbe188260?se=202
######################################################################## 100.0%
==> Fetching mpfr
==> Downloading https://ghcr.io/v2/homebrew/core/mpfr/manifests/4.2.0
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/mpfr/blobs/sha256:3e62ca94c057c73dfb08c6b7be42b3aaf0e338c9a422dbdd74695e1106c302a5
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:3e62ca94c057c73dfb08c6b7be42b3aaf0e338c9a422dbdd74695e1106c302a5?se=202
######################################################################## 100.0%
==> Fetching libmpc
==> Downloading https://ghcr.io/v2/homebrew/core/libmpc/manifests/1.3.1
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/libmpc/blobs/sha256:f6542ae5bcf643ca0c980c7000cde1585922e76be080b3cc3422dac0d4a50904
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:f6542ae5bcf643ca0c980c7000cde1585922e76be080b3cc3422dac0d4a50904?se=202
######################################################################## 100.0%
==> Fetching xz
==> Downloading https://ghcr.io/v2/homebrew/core/xz/manifests/5.4.1
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/xz/blobs/sha256:a2e27545d92f5387a48182319b5233a42c9ce98bea335fc4cdf15c808ab1ea1b
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:a2e27545d92f5387a48182319b5233a42c9ce98bea335fc4cdf15c808ab1ea1b?se=202
######################################################################## 100.0%
==> Fetching zlib
==> Downloading https://ghcr.io/v2/homebrew/core/zlib/manifests/1.2.13
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/zlib/blobs/sha256:0082aa29a61507e237389ee4e9fb6a6ed0cbd5d341e3905527c089c88e730411
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:0082aa29a61507e237389ee4e9fb6a6ed0cbd5d341e3905527c089c88e730411?se=202
######################################################################## 100.0%
==> Fetching binutils
==> Downloading https://ghcr.io/v2/homebrew/core/binutils/manifests/2.40
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/binutils/blobs/sha256:f0917ab2fe7b72350deac1946dd8e937e0199292f05ab763c5f309822c04c195
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:f0917ab2fe7b72350deac1946dd8e937e0199292f05ab763c5f309822c04c195?se=202
######################################################################## 100.0%
==> Fetching hugo
==> Downloading https://ghcr.io/v2/homebrew/core/hugo/manifests/0.110.0
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/hugo/blobs/sha256:4cb16974713723501f6e6035da715621691f9afe6aa8cce902c408a205f5f2cd
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:4cb16974713723501f6e6035da715621691f9afe6aa8cce902c408a205f5f2cd?se=202
######################################################################## 100.0%
==> Installing dependencies for hugo: linux-headers@5.15, mpfr, libmpc, xz, zlib and binutils
==> Installing hugo dependency: linux-headers@5.15
==> Pouring linux-headers@5.15--5.15.91.x86_64_linux.bottle.tar.gz
🍺  /home/linuxbrew/.linuxbrew/Cellar/linux-headers@5.15/5.15.91: 963 files, 5.7MB
==> Installing hugo dependency: mpfr
==> Pouring mpfr--4.2.0.x86_64_linux.bottle.tar.gz
🍺  /home/linuxbrew/.linuxbrew/Cellar/mpfr/4.2.0: 31 files, 3.9MB
==> Installing hugo dependency: libmpc
==> Pouring libmpc--1.3.1.x86_64_linux.bottle.tar.gz
🍺  /home/linuxbrew/.linuxbrew/Cellar/libmpc/1.3.1: 13 files, 638.6KB
==> Installing hugo dependency: xz
==> Pouring xz--5.4.1.x86_64_linux.bottle.tar.gz
🍺  /home/linuxbrew/.linuxbrew/Cellar/xz/5.4.1: 223 files, 3MB
==> Installing hugo dependency: zlib
==> Pouring zlib--1.2.13.x86_64_linux.bottle.tar.gz
🍺  /home/linuxbrew/.linuxbrew/Cellar/zlib/1.2.13: 13 files, 472.7KB
==> Installing hugo dependency: binutils
==> Pouring binutils--2.40.x86_64_linux.bottle.tar.gz
🍺  /home/linuxbrew/.linuxbrew/Cellar/binutils/2.40: 4,698 files, 462.5MB
==> Installing hugo
==> Pouring hugo--0.110.0.x86_64_linux.bottle.tar.gz
==> Caveats
Bash completion has been installed to:
  /home/linuxbrew/.linuxbrew/etc/bash_completion.d
==> Summary
🍺  /home/linuxbrew/.linuxbrew/Cellar/hugo/0.110.0: 48 files, 53.4MB
==> Running `brew cleanup hugo`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
Warning: The following dependents of upgraded formulae are outdated but will not
be upgraded because they are not bottled:
  cf2
  kn
  quickstart
==> Upgrading 3 dependents of upgraded formulae:
Disable this behaviour by setting HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
go 1.19.1 -> 1.19.5, k9s 0.26.6 -> 0.27.2, kubernetes-cli 1.25.2 -> 1.26.1
==> Fetching go
==> Downloading https://ghcr.io/v2/homebrew/core/go/manifests/1.19.5
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/go/blobs/sha256:4060878a09c55ce7bd3b1093829a5e01622be31816c368f8fcaf07e38bca1fb9
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:4060878a09c55ce7bd3b1093829a5e01622be31816c368f8fcaf07e38bca1fb9?se=202
######################################################################## 100.0%
==> Fetching k9s
==> Downloading https://ghcr.io/v2/homebrew/core/k9s/manifests/0.27.2
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/k9s/blobs/sha256:a44ee7532ca79e8635c07cf7ac3b4a08f44b004ed1d968891b927ad9aee8801c
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:a44ee7532ca79e8635c07cf7ac3b4a08f44b004ed1d968891b927ad9aee8801c?se=202
######################################################################## 100.0%
==> Fetching kubernetes-cli
==> Downloading https://ghcr.io/v2/homebrew/core/kubernetes-cli/manifests/1.26.1
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/kubernetes-cli/blobs/sha256:69d5374efc9321dc5c312e4d434b17d9b11c05e66aec480751d2ea23ef34947e
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:69d5374efc9321dc5c312e4d434b17d9b11c05e66aec480751d2ea23ef34947e?se=202
######################################################################## 100.0%
==> Upgrading go
  1.19.1 -> 1.19.5

==> Pouring go--1.19.5.x86_64_linux.bottle.tar.gz
🍺  /home/linuxbrew/.linuxbrew/Cellar/go/1.19.5: 12,456 files, 592.6MB
==> Running `brew cleanup go`...
Removing: /home/linuxbrew/.linuxbrew/Cellar/go/1.19.1... (12,434 files, 591.9MB)
Removing: /home/builder/.cache/Homebrew/go--1.19.1... (176.8MB)
==> Upgrading k9s
  0.26.6 -> 0.27.2

==> Pouring k9s--0.27.2.x86_64_linux.bottle.tar.gz
==> Caveats
Bash completion has been installed to:
  /home/linuxbrew/.linuxbrew/etc/bash_completion.d
==> Summary
🍺  /home/linuxbrew/.linuxbrew/Cellar/k9s/0.27.2: 9 files, 72.3MB
==> Running `brew cleanup k9s`...
Removing: /home/linuxbrew/.linuxbrew/Cellar/k9s/0.26.6... (9 files, 69.6MB)
Removing: /home/builder/.cache/Homebrew/k9s--0.26.6... (21.9MB)
==> Upgrading kubernetes-cli
  1.25.2 -> 1.26.1

==> Pouring kubernetes-cli--1.26.1.x86_64_linux.bottle.tar.gz
🍺  /home/linuxbrew/.linuxbrew/Cellar/kubernetes-cli/1.26.1: 231 files, 46.8MB
==> Running `brew cleanup kubernetes-cli`...
Removing: /home/linuxbrew/.linuxbrew/Cellar/kubernetes-cli/1.25.2... (228 files, 43.9MB)
Removing: /home/builder/.cache/Homebrew/kubernetes-cli--1.25.2... (13.6MB)
==> Checking for dependents of upgraded formulae...
==> No broken dependents found!
==> `brew cleanup` has not been run in the last 30 days, running now...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
Removing: /home/linuxbrew/.linuxbrew/Cellar/binutils/2.39_1... (4,687 files, 370.5MB)
Removing: /home/builder/.cache/Homebrew/binutils--2.39_1... (70.7MB)
Removing: /home/builder/.cache/Homebrew/cf2--git... (193 files, 3MB)
Removing: /home/builder/.cache/Homebrew/gcc--12.2.0... (105.8MB)
Removing: /home/builder/.cache/Homebrew/glibc--2.35_1... (14MB)
Removing: /home/builder/.cache/Homebrew/gmp--6.2.1_1... (1.1MB)
Removing: /home/builder/.cache/Homebrew/isl--0.25... (2MB)
Removing: /home/builder/.cache/Homebrew/kn--knative-v1.7.0... (56MB)
Removing: /home/builder/.cache/Homebrew/kubectx--0.9.4... (11.3KB)
Removing: /home/linuxbrew/.linuxbrew/Cellar/libmpc/1.2.1... (13 files, 550.2KB)
Removing: /home/builder/.cache/Homebrew/libmpc--1.2.1... (147KB)
Removing: /home/linuxbrew/.linuxbrew/Cellar/linux-headers@5.15/5.15.57... (963 files, 5.7MB)
Removing: /home/builder/.cache/Homebrew/linux-headers@5.15--5.15.57... (1.5MB)
Removing: /home/builder/.cache/Homebrew/lz4--1.9.4... (277.5KB)
Removing: /home/linuxbrew/.linuxbrew/Cellar/mpfr/4.1.0... (31 files, 7.9MB)
Removing: /home/builder/.cache/Homebrew/mpfr--4.1.0... (3.2MB)
Removing: /home/builder/.cache/Homebrew/quickstart--v1.7.0... (4.3MB)
Removing: /home/linuxbrew/.linuxbrew/Cellar/xz/5.2.7... (151 files, 2.5MB)
Removing: /home/builder/.cache/Homebrew/xz--5.2.7... (666.9KB)
Removing: /home/linuxbrew/.linuxbrew/Cellar/zlib/1.2.12_1... (12 files, 470.7KB)
Removing: /home/builder/.cache/Homebrew/zlib--1.2.12_1... (170.3KB)
Removing: /home/builder/.cache/Homebrew/zstd--1.5.2... (967.4KB)
Removing: /home/builder/.cache/Homebrew/kubernetes-cli_bottle_manifest--1.25.2... (6.1KB)
Removing: /home/builder/.cache/Homebrew/isl_bottle_manifest--0.25... (6.7KB)
Removing: /home/builder/.cache/Homebrew/xz_bottle_manifest--5.2.6... (6.4KB)
Removing: /home/builder/.cache/Homebrew/mpfr_bottle_manifest--4.1.0... (8.0KB)
Removing: /home/builder/.cache/Homebrew/linux-headers@5.15_bottle_manifest--5.15.57-1... (1.9KB)
Removing: /home/builder/.cache/Homebrew/glibc_bottle_manifest--2.35_1... (2KB)
Removing: /home/builder/.cache/Homebrew/portable-ruby-2.6.8_1.x86_64_linux.bottle.tar.gz... (10.3MB)
Removing: /home/builder/.cache/Homebrew/binutils_bottle_manifest--2.39_1... (10.5KB)
Removing: /home/builder/.cache/Homebrew/kubectx_bottle_manifest--0.9.4... (1.9KB)
Removing: /home/builder/.cache/Homebrew/lz4_bottle_manifest--1.9.4... (6.3KB)
Removing: /home/builder/.cache/Homebrew/go_mod_cache... (45,445 files, 798.2MB)
Removing: /home/builder/.cache/Homebrew/kubernetes-cli_bottle_manifest--1.24.2... (6.1KB)
Removing: /home/builder/.cache/Homebrew/descriptions.json... (344.7KB)
Removing: /home/builder/.cache/Homebrew/go_cache... (7,324 files, 839.6MB)
Removing: /home/builder/.cache/Homebrew/xz_bottle_manifest--5.2.7... (6.4KB)
Removing: /home/builder/.cache/Homebrew/k9s_bottle_manifest--0.26.6... (6.1KB)
Removing: /home/builder/.cache/Homebrew/go_bottle_manifest--1.18.4... (7.3KB)
Removing: /home/builder/.cache/Homebrew/zlib_bottle_manifest--1.2.12_1... (6.3KB)
Removing: /home/builder/.cache/Homebrew/gcc_bottle_manifest--12.2.0-1... (10.8KB)
Removing: /home/builder/.cache/Homebrew/zstd_bottle_manifest--1.5.2-3... (7.2KB)
Removing: /home/builder/.cache/Homebrew/gmp_bottle_manifest--6.2.1_1... (7.4KB)
Removing: /home/builder/.cache/Homebrew/libmpc_bottle_manifest--1.2.1... (8.6KB)
Removing: /home/builder/.cache/Homebrew/go_bottle_manifest--1.19.1... (7.6KB)
Removing: /home/builder/.cache/Homebrew/Logs/glibc... (2 files, 4.2KB)
Removing: /home/builder/.cache/Homebrew/Logs/cf2... (2 files, 71.3KB)
Removing: /home/builder/.cache/Homebrew/Logs/kn... (4KB)
Removing: /home/builder/.cache/Homebrew/Logs/quickstart... (4KB)
Pruned 2 symbolic links from /home/linuxbrew/.linuxbrew
==> Caveats
==> hugo
Bash completion has been installed to:
  /home/linuxbrew/.linuxbrew/etc/bash_completion.d
==> k9s
Bash completion has been installed to:
  /home/linuxbrew/.linuxbrew/etc/bash_completion.d

Now we can launch hugo

builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ hugo server
Start building sites …
hugo v0.110.0+extended linux/amd64 BuildDate=unknown

                   | EN
-------------------+-----
  Pages            |  7
  Paginator pages  |  0
  Non-page files   |  0
  Static files     |  7
  Processed images |  0
  Aliases          |  1
  Sitemaps         |  1
  Cleaned          |  0

Built in 160 ms
Watching for changes in /home/builder/Workspaces/hugoBlog/myBlog/{archetypes,content,data,layouts,static,themes}
Watching for config changes in /home/builder/Workspaces/hugoBlog/myBlog/config.toml, /home/builder/Workspaces/hugoBlog/myBlog/themes/terminal/config.toml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop

/content/images/2023/02/hugo-01.png

Contentful

I’ll now add contentful-hugo to pull in Contentful content

builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ npm install contentful-hugo

added 131 packages, and audited 132 packages in 13s

16 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npm notice
npm notice New major version of npm available! 8.5.1 -> 9.4.1
npm notice Changelog: https://github.com/npm/cli/releases/tag/v9.4.1
npm notice Run npm install -g npm@9.4.1 to update!
npm notice

builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ node_modules/.bin/contentful-hugo –init checking for config… creating ./contentful-hugo.config.js config file created

adding shortcodes for rich text… created ./layouts/shortcodes/contentful-hugo/asset-hyperlink.html created ./layouts/shortcodes/contentful-hugo/embedded-asset.html created ./layouts/shortcodes/contentful-hugo/embedded-entry.html created ./layouts/shortcodes/contentful-hugo/entry-hyperlink.html created ./layouts/shortcodes/contentful-hugo/inline-entry.html

export CONTENTFUL_SPACE=”ukkohuhcdmei” export CONTENTFUL_TOKEN=”zu-_NRcOoZoflWogcgGcEQpgkRpEE5wlxGqCIc0Lj3M” export CONTENTFUL_PREVIEW_TOKEN=”7I_NI-xL9CRbdGjmUiviVUjjx5HsgA5H7QE6ZzShMOs”

Now we can pull in our pageBlogPosts

// go to https://github.com/ModiiMedia/contentful-hugo#configuration for configuration instructions

/**
 * @type {import('contentful-hugo').ContentfulHugoConfig}
 */
module.exports = {
    locales: [], // uses default locale if left empty
    singleTypes: [],
    repeatableTypes: [
        {
            id: 'pageBlogPost',
            directory: 'content/posts',
            mainContent: 'content',
            resolveEntries: {
                categories: 'fields.slug',
                author: 'fields.name',
                relatedPosts: 'sys.id',
            },
        },
    ],
};

This will work as we set our Environment Variables.

However, we could embed the values as well


module.exports = {
    // fetches from default locale if left blank
    locales: ['en-US', 'fr-FR'],

    contentful: {
        // defaults to CONTENTFUL_SPACE env variable
        space: 'space-id',
        // defaults to CONTENTFUL_TOKEN env variable
        token: 'content-deliver-token',
        // defaults to CONTENTFUL_PREVIEW_TOKEN env variable
        previewToken: 'content-preview-token',
        // defaults to "master"
        environment: 'master',
    },

Now when we run it

builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ npm run dev

> dev
> contentful-hugo --preview && hugo server


---------------------------------------------
   Pulling Preview Data from Contentful...
---------------------------------------------

   pageBlogPost - 8 items

---------------------------------------------

Start building sites …
hugo v0.110.0+extended linux/amd64 BuildDate=unknown

                   | EN
-------------------+-----
  Pages            | 17
  Paginator pages  |  0
  Non-page files   |  0
  Static files     |  7
  Processed images |  0
  Aliases          |  2
  Sitemaps         |  1
  Cleaned          |  0

Built in 106 ms
Watching for changes in /home/builder/Workspaces/hugoBlog/myBlog/{archetypes,content,data,layouts,package.json,static,themes}
Watching for config changes in /home/builder/Workspaces/hugoBlog/myBlog/config.toml, /home/builder/Workspaces/hugoBlog/myBlog/themes/terminal/config.toml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop

/content/images/2023/02/hugo-022.png

And we can view individual posts

/content/images/2023/02/hugo-03.png

CICD

Let’s first create a Github repo we can use to host the code

/content/images/2023/02/hugo-04.png

In my Settings, I’ll add secrets for the Contentful environment.

As a reminder, we see those in “API Keys” in Settings on Contentful

/content/images/2023/02/hugo-05.png

If you have more than one space, you can use the ones tied to “Blog”

/content/images/2023/02/hugo-06.png

I now have secrets for the Space, Delivery and Preview Tokens

/content/images/2023/02/hugo-07.png

I’ll make a .gitignore file

builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ cat .gitignore
# Created by https://www.toptal.com/developers/gitignore/api/hugo,linux,visualstudiocode,nodejs
# Edit at https://www.toptal.com/developers/gitignore?templates=hugo,linux,visualstudiocode,nodejs

### Hugo ###
# Generated files by hugo
/public/
/resources/_gen/
/assets/jsconfig.json
hugo_stats.json

# Executable may be added to repository
hugo.exe
hugo.darwin
hugo.linux

# Temporary lock file while building
/.hugo_build.lock

### Linux ###
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

#!! ERROR: nodejs is undefined. Use list command to see defined gitignore types !!#

### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets

# Local History for Visual Studio Code
.history/

# Built Visual Studio Code Extensions
*.vsix

### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide

themes/

# End of https://www.toptal.com/developers/gitignore/api/hugo,linux,visualstudiocode,nodejs

Then add and push my existing repo up

builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ git remote add origin https://github.com/idjohnson/myNewBlog.git
builder@DESKTOP-72D2D9T:~/Workspaces/hugoBlog/myBlog$ git push -u origin main
Enumerating objects: 29, done.
Counting objects: 100% (29/29), done.
Delta compression using up to 4 threads
Compressing objects: 100% (24/24), done.
Writing objects: 100% (29/29), 41.63 KiB | 4.16 MiB/s, done.
Total 29 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), done.
To https://github.com/idjohnson/myNewBlog.git
 * [new branch]      main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.

Now that I have some content in my repo

/content/images/2023/02/hugo-08.png

I’ll create the Github Actions build that will drive static site creation

There are a few ways we can add Hugo to the Github Actions Runner. Using apt install will get us the same old binary as before, so we don’t want to use that route.

You can use homebrew to easily add it:

      - name: Install Hugo
        run: |
          umask 0002

          test -d ~/.linuxbrew && eval "$(~/.linuxbrew/bin/brew shellenv)"
          test -d /home/linuxbrew/.linuxbrew && eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
          test -r ~/.bash_profile && echo "eval \"\$($(brew --prefix)/bin/brew shellenv)\"" >> ~/.bash_profile
          echo "eval \"\$($(brew --prefix)/bin/brew shellenv)\"" >> ~/.profile

          brew install hugo

Or a Debian package, which will pin it to a specific release

      - name: Install Hugo
        run: |
          umask 0002

          sudo apt-get update -y
          wget https://github.com/gohugoio/hugo/releases/download/v0.79.0/hugo_0.79.0_Linux-64bit.deb
          sudo dpkg -i hugo_0.79.0_Linux-64bit.deb

I used both, but stuck with the latter. My first shot at generating looked as such

$ cat .github/workflows/generate-site.yml
jobs:
  build_deploy_test:
    runs-on: ubuntu-latest
    steps:
      - name: Check out repository code
        uses: actions/checkout@v2
      - name: Install Hugo
        run: |
          umask 0002

          sudo apt-get update -y
          wget https://github.com/gohugoio/hugo/releases/download/v0.79.0/hugo_0.79.0_Linux-64bit.deb
          sudo dpkg -i hugo_0.79.0_Linux-64bit.deb

          sudo apt-get install -y tree
      - name: Hugo Build
        run: |
          npm install
          npm run build
          tree .
        env: # Contentful Tokens
          CONTENTFUL_SPACE: $
          CONTENTFUL_TOKEN: $
          CONTENTFUL_PREVIEW_TOKEN: $

We can see in our build output the results of the tree command that lists our directory. We can see it built out the markdown for our Contentful posts

/content/images/2023/02/hugo-09.png

Azure Static Web App

The Static Web App (or Site), is a way we can host this generated content in Azure for low to no cost.

I’ll go to the Azure Portal and “+ Add a Resource” and search for “Static Web App”

/content/images/2023/02/hugo-10.png

and click “Create”

/content/images/2023/02/hugo-11.png

I’ll create a fresh Resource Group

/content/images/2023/02/hugo-12.png

Then give it a name and select (if not selected already) the Free tier. I’ll then want to click “Sign in with GitHub”

/content/images/2023/02/hugo-13.png

And grant access to my account (and possibly org)

/content/images/2023/02/hugo-14.png

I can now select the Organization

/content/images/2023/02/hugo-15.png

In the “Build Presets”, I’ll select “Hugo”

/content/images/2023/02/hugo-16.png

When I click “Preview workflow file”, I can now see some example code we can use

/content/images/2023/02/hugo-17.png

I’ll set that aside and click “Review and Create” then lastly, “Create” to create the Static Site

/content/images/2023/02/hugo-18.png

When that was created, it updated our Github repo for us automatically. We can now see a new flow in the system

/content/images/2023/02/hugo-19.png

Not suprisingly, that errored because it is really designed for a basic Hugo setup, not a Contentful driven one

/content/images/2023/02/hugo-20.png

I’ll use git pull locally to bring in the new file

builder@DESKTOP-QADGF36:~/Workspaces/myNewBlog$ git pull
remote: Enumerating objects: 8, done.
remote: Counting objects: 100% (8/8), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 5 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), 1.16 KiB | 1.16 MiB/s, done.
From https://github.com/idjohnson/myNewBlog
   18b50a8..5b251ef  main       -> origin/main
Updating 18b50a8..5b251ef
Fast-forward
 .github/workflows/azure-static-web-apps-purple-bush-00ab46010.yml | 45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 .github/workflows/azure-static-web-apps-purple-bush-00ab46010.yml

The key change is to add the CONTENTFUL secrets

/content/images/2023/02/hugo-21.png

While we test the new flow, I’ll disable the former (just so we don’t waste time doing double builds)

/content/images/2023/02/hugo-22.png

We can see our change of adding the env secrets sorted out the static website build

/content/images/2023/02/hugo-23.png

At the bottom of the log, we can see our URL; https://purple-bush-00ab46010.2.azurestaticapps.net

/content/images/2023/02/hugo-24.png

I can see one issue right out, that is the Domain needs to be adjusted

/content/images/2023/02/hugo-25.png

That is set in the ‘baseURL’ of config.toml

$ cat config.toml
baseURL = "https://purple-bush-00ab46010.2.azurestaticapps.net/"
languageCode = "en-us"
title = "My New Hugo Site"
theme = 'terminal'

Once, changed, our freshly built site looks correct

/content/images/2023/02/hugo-27.png

And we can view individual posts without issue

/content/images/2023/02/hugo-28.png

Azure Stats

We can view our usage back on the Azure Portal.

This includes metrics like Requests and Data egress

/content/images/2023/02/hugo-29.png

Say we wish to use a custom domain. Nothing wrong with an ‘azurestaticapps’ site, but it would be nice for something a bit shorter.

I’ll check to see if I have any active DNS zones in Azure already (and I do)

/content/images/2023/02/hugo-30.png

But for fun, let’s add a new one. We can see I registered a “tpk.life” domain for fun a few weeks ago in Gandi.

/content/images/2023/02/hugo-31.png

I’ll click add and put in the details

/content/images/2023/02/hugo-32.png

After creation, we can see the resource for the records we need to change in order to use the Hosted Zone in Azure DNS

/content/images/2023/02/hugo-33.png

In Gandi, I’ll just change from Gandi’s DNS to Azure

/content/images/2023/02/hugo-34.png

Then save

/content/images/2023/02/hugo-35.png

This can take up to 24h to update. That said, I can use the one (tpk.pw) that has already been validated for some time

We’ll go back to the Static Web App, in Settings/Custom domains and choose to “+ Add” a “Custom domain on Azure DNS”

/content/images/2023/02/hugo-36.png

I’ll give it a name

/content/images/2023/02/hugo-37.png

Then click “Add”

/content/images/2023/02/hugo-38.png

While it seems to have gone out to lunch on the Azure Portal UI, I can already see the change reflected on the site

/content/images/2023/02/hugo-39.png

I’ll change my URL in the config and push a commit

builder@DESKTOP-QADGF36:~/Workspaces/myNewBlog$ git diff config.toml
diff --git a/config.toml b/config.toml
index ba4fff6..bb0425f 100644
--- a/config.toml
+++ b/config.toml
@@ -1,4 +1,4 @@
-baseURL = "https://purple-bush-00ab46010.2.azurestaticapps.net/"
+baseURL = "https://newblog.tpk.pw/"
 languageCode = "en-us"
 title = "My New Hugo Site"
 theme = 'terminal'

builder@DESKTOP-QADGF36:~/Workspaces/myNewBlog$ git add config.toml 
builder@DESKTOP-QADGF36:~/Workspaces/myNewBlog$ git commit -m "change to new URL"
[main 34e59c6] change to new URL
 1 file changed, 1 insertion(+), 1 deletion(-)
builder@DESKTOP-QADGF36:~/Workspaces/myNewBlog$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 16 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 368 bytes | 368.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/idjohnson/myNewBlog.git
   da6c401..34e59c6  main -> main

One the Workflow completed, I can see the proper URLs in the site

/content/images/2023/02/hugo-40.png

Triggering from Contentful

To trigger a Workflow externally, in Github we’ll need to add a “dispatch”.

We can add a trigger for dispatches then a specific type (webhook) in our Github Workflow YAML

name: Azure Static Web Apps CI/CD

on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize, reopened, closed]
    branches:
      - main
  workflow_dispatch:

  repository_dispatch:
    types:
      - webhook

jobs:

/content/images/2023/02/hugo-43.png

In Contentful, to use the Dispatch webhook, We first go to Settings / Webhooks

/content/images/2023/02/hugo-41.png

Oddly they have every CI except Github listed in the Templates

I’ll choose “Add Webhook”

/content/images/2023/02/hugo-42.png

I add a URL that adds “/dispatches” to the Github URL, then a custom header for “Accept” set to “application/vnd.github+json”.

/content/images/2023/02/hugo-45.png

For the “+ Secret header”, I’ll want to add a Key of “Authorization” and value of “Bearer {personal token with repo access}”

Something like ‘Bearer ghp_AE5asdfE4Asadf54AEasdf545AasdfE’

/content/images/2023/02/hugo-44.png

Lastly, because we specified a type (webhook), we’ll want to add that in the payload

/content/images/2023/02/hugo-46.png

The resulting Webhook definition should look similar to this. (see below, we end up tweaking this in the end)

/content/images/2023/02/hugo-47.png

Right now, I have it set to “All Events”. But arguably, we could trim it to just Entry changes

/content/images/2023/02/hugo-48.png

Once saves, we can see it listed as “Active”

/content/images/2023/02/hugo-49.png

Triggering the Workflow

Let’s add a quick blog entry

/content/images/2023/02/hugo-50.png

I won’t make it too fancy, just a simple Hello World will do

/content/images/2023/02/hugo-51.png

I’ll click “Publish” which will change the status

/content/images/2023/02/hugo-52.png

However, my webhook failed

/content/images/2023/02/hugo-53.png

It took a while, the REST API had changed over time and the guide I followed was not up to date.

The corrected webhook used “Bearer” not “token” in the Authorization and whole different URL:

"https://api.github.com/repos/{ org or owner }/{ repo name }/actions/workflows/{ workflow name or id }/dispatches"

e.g.

"https://api.github.com/repos/idjohnson/myNewBlog/actions/workflows/azure-static-web-apps-purple-bush-00ab46010.yml/dispatches"

Thus, the full definition of the resulting (successful) webhook looked like:

/content/images/2023/02/hugo-55.png

which triggered successfully from Contentful

/content/images/2023/02/hugo-54.png

and queued in Github

/content/images/2023/02/hugo-56.png

The problem, I found was that template as deployed by Azure only allowed PRs and Push events. We need to update it to allow workflow_dispatch as well:

jobs:
  build_and_deploy_job:
    if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || (github.event_name == 'pull_request' && github.event.action != 'closed')
    runs-on: ubuntu-latest
    name: Build and Deploy Job

We can now watch the full workflow in action (however, I clearly need to limit the steps to just publish actions as you’ll see)

Limitting the events

I decided I should attempt to clean up the events triggering GH. I reduced it to pubish/unpublish on Content Types and Entrys.

/content/images/2023/02/hugo-65.png

We can see that in action below

Automated Reports

We can setup a “Task” in the Static site to send us reports. Select Tasks under Automation

/content/images/2023/02/hugo-58.png

Add a task

/content/images/2023/02/hugo-59.png

Here we’ll use the only template presently available for monthly reports

/content/images/2023/02/hugo-60.png

I can use ARM or O365 for authentication. (actually i needed to go back and setup both)

/content/images/2023/02/hugo-61.png

I’ll set a destination

/content/images/2023/02/hugo-62.png

It does not that it’s billable, which means the background Azure Logic App will probably cost a small amount to run.

We can see that is what it intends to make on the Review and Create page

/content/images/2023/02/hugo-63.png

I now see a scheduled task listed

/content/images/2023/02/hugo-64.png

Summary

I should note that before I realized the Icon was a Gopher, I figured it to be a Hamster. Thus, I had a bit of art that I think is too cute to not share:

/content/images/2023/02/isaacjohnson_a_soft_light_blue_hampster_with_white_buck_teeth_w_abb798d6-8366-4f9d-bb8c-7c4a805a3783.png

Let’s review what we covered today. We created a new Github Repository and a new NodeJS project. We setup Hugo locally and created the contentful-hugo connection using some environment variables as the contentful-hugo node module. After testing, we setup a Github Workflow to build our project then tied it to a new Azure Static Web App site. There we added a custom domain though an Azure DNS hosted zone. Lastly, we worked out the Github “dispatcher” webhook trigger and tied it to Contentful to deploy on Contentful events.

Provided we keep the workspace under 500Mb and are happy with 2 domains an App, we’ll live just dandy in the free tier.

/content/images/2023/02/hugo-57.png

Thus far, my costs have stayed at US$0 in Azure

/content/images/2023/02/hugo-67.png

cms contentful hugo github Azure webhooks

Have something to add? Feedback? Try our new forums

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes