1
0
Fork 0
Website of Nick Schmidt (oneguynick) https://geekyschmidt.com
  • JavaScript 99.4%
  • CSS 0.4%
  • HTML 0.2%
Find a file
Nicholas Schmidt 3f5ce9308d
All checks were successful
Build and Deploy Hugo Site / hugo-build (push) Successful in 2m0s
2 New posts for the week
2026-07-03 23:10:01 +00:00
.forgejo/workflows remove Truffle 2026-05-27 15:04:04 +00:00
content 2 New posts for the week 2026-07-03 23:10:01 +00:00
data Add website 2026-05-25 23:28:33 +00:00
layouts add categories fix 2026-06-02 22:33:43 +00:00
static 2 New posts for the week 2026-06-28 14:04:12 +00:00
themes ci: complete removal of orphan m10c theme directory from tree 2026-05-26 12:58:16 +00:00
.gitignore Add website 2026-05-25 23:28:33 +00:00
.gitmodules fix: purge orphan hugo-theme-m10c submodule reference 2026-05-26 12:01:27 +00:00
hugo.toml fix for categories 2026-06-02 19:21:16 +00:00
hugo.toml.orig ci: Improve hugo.toml 2026-05-27 13:48:19 +00:00
Makefile Add website 2026-05-25 23:28:33 +00:00
README.md Truffle 2026-05-27 14:38:34 +00:00

Hugo GitOps Pipeline with Upstream Monitored Apps & Security Scanning

A production-hardened, self-hosted CI/CD GitOps workflow for deploying an optimized Hugo static site. Built for deployment via native Docker runners (Forgejo/Gitea Actions, GitHub Actions), this setup dynamically aggregates upstream decentralized applications, handles advanced static assets, executes security audits, and securely deploys over a local network loop.

Architecture Overview

This architecture is engineered for speed, security, and digital sovereignty. The build phase operates inside an ephemeral network sandbox container, processes asset transformations, and syncs directly to the host's web root container volume over an internally routed SSH gate.

Core Optimization Features

  • Upstream Client Syncing: Dynamically queries GitHub APIs to intercept, filter, and extract client-side single-page applications (such as FluffyChat for Matrix communication and Phanpy for Mastodon interactions) into specified site subdirectories.
  • Static Search Engine: Integrated with Pagefind, compiling low-bandwidth, privacy-respecting client-side search fragments instantly post-build. No external server tracking required.
  • Asset Pipeline (Hugo Pipes): Automatically minifies, bundles, and applies cryptographic fingerprint integrity hashes to site cascading stylesheets.
  • Next-Gen Media Handling: Custom layout shortcodes automatically transcode uploaded images into heavily compressed .webp and .avif formats dynamically at compile time.
  • Airtight Security: Implements native Go runtime vulnerability audits (govulncheck) directly inside the build environment step before compiling code.
  • High-Ratio Pre-Compression: Automatically runs a parallelized search execution loop finding all text-based deliverables and pre-compiling maximum-level (gzip -9kf) compression layer twins for server-side static acceleration blocks.

Configuration Blueprints

1. The Global Site Matrix (hugo.toml)

This configuration registers structural parameters, locks down native server-side syntax highlighting themes (Chroma), sets up Goldmark HTML rendering boundaries, and initializes dynamic custom tracking values.

baseURL = "[https://geekyschmidt.com](https://geekyschmidt.com)"
title = ""
locale = "en"
defaultContentLanguage = "en"
theme = "PaperMod"

# Tactical silencing of compilation warnings
ignoreLogs = [
    "warning-goldmark-raw-html",
    "deprecated"
]

copyright = "Copyright ©2002-2026, Nicholas Schmidt; all rights reserved."
enableRobotsTXT = true
enableInlineShortcodes = true
enableEmoji = true

[pagination]
  pagerSize = 5

[outputs]
  home = ["HTML", "RSS", "JSON"]
  page = ["HTML"]

[services]
  [services.instagram]
    disableInlineCSS = true
  [services.rss]
    limit = 10
  [services.x]
    disableInlineCSS = true

[minify]
  minifyOutput = true
  [minify.tdewolff]
    [minify.tdewolff.html]
      keepEndTags = true
      keepWhitespace = false

[markup]
  [markup.goldmark]
    [markup.goldmark.renderer]
      unsafe = true # Required for embedded raw HTML rendering
  
  # NATIVE CHROMA SYNTAX HIGHLIGHTING MATRIX
  [markup.highlight]
    codeFences = true
    guessSyntax = true
    lineNos = true
    noClasses = false
    style = "monokai"

[menu]
  [[menu.main]]
    identifier = "home"
    name = "Home"
    url = "/"
    weight = 1
  [[menu.main]]
    identifier = "about"
    name = "About"
    url = "/about/"
    weight = 2
  [[menu.main]]
    identifier = "resume"
    name = "Resume"
    url = "/cv/"
    weight = 3
  [[menu.main]]
    identifier = "search"
    name = "Search"
    url = "/search/"
    weight = 0
  [[menu.main]]
    identifier = "categories"
    name = "Categories"
    url = "/categories/"
    weight = 4

[params]
  author = "Nick Schmidt (oneguynick)"
  description = "Dad, CTO, and lover of all things 1's and 0's."
  defaultTheme = "auto"

  # Theme UI features
  ShowToc = true
  ShowReadingTime = true
  ShowBreadCrumbs = true
  ShowCodeCopyButtons = true
  ShowRssButtonInSectionTermList = true
  ShowPageNums = true
  ShowPostNavLinks = true

  fuseOpts = { isCaseSensitive = false, shouldSort = true, location = 0, distance = 100, threshold = 0.4, minMatchCharLength = 0, keys = ["title", "permalink", "summary", "content"] }
  images = ["images/papermod-cover.png"]

  # PRIVACY-RESPECTING TRACKER TARGETS
  umamiScriptUrl = "[https://your-umami-instance.domain.com/script.js](https://your-umami-instance.domain.com/script.js)"
  umamiWebsiteId = "your-unique-website-uuid"

  [params.assets]
    disableHLJS = true
    favicon = "favicon.ico"

  [params.profileMode]
    enabled = true
    title = "Nick Schmidt"
    subtitle = "Dad, CTO, and lover of all things 1's and 0's."
    imageUrl = "assets/images/avatar.webp"
    imageTitle = "Avatar"
    imageWidth = 120
    imageHeight = 120
    [[params.profileMode.buttons]]
      name = "Archives"
      url = "archives"
    [[params.profileMode.buttons]]
      name = "Categories"
      url = "categories"

  [params.homeInfoParams]
    Title = "Howdy 👋"
    Content = "Welcome to my personal blog."

  [[params.socialIcons]]
    name = "gitlab"
    url = "[https://gitlab.com/oneguynick](https://gitlab.com/oneguynick)"
  [[params.socialIcons]]
    name = "mastodon"
    url = "[https://treffen.geekyschmidt.com/@nick](https://treffen.geekyschmidt.com/@nick)"
  [[params.socialIcons]]
    name = "matrix"
    url = "[https://matrix.to/#/@nick:geekyschmidt.com](https://matrix.to/#/@nick:geekyschmidt.com)"
  [[params.socialIcons]]
    name = "linkedin"
    url = "[https://www.linkedin.com/in/oneguynick](https://www.linkedin.com/in/oneguynick)"
  [[params.socialIcons]]
    name = "email"
    url = "mailto:nick@geekyschmidt.com"
  [[params.socialIcons]]
    name = "key"
    url = "/pgp.asc"

Embedded Extension Layouts

To map analytics and script injections flawlessly without breaking the upstream theme configuration files, deploy these targeted layouts inside your layouts/ template directory tree:

Header Extension (layouts/partials/extend_head.html)

Handles safe conditional loading for your cookies-free Umami Tracking Module:

{{ if and .Site.Params.umamiScriptUrl .Site.Params.umamiWebsiteId }}
  <script async src="{{ .Site.Params.umamiScriptUrl }}" data-website-id="{{ .Site.Params.umamiWebsiteId }}"></script>
{{ end }}

Media Extension (layouts/shortcodes/img.html)

{{ $img := .Page.Resources.GetMatch (.Get "src") }}
{{ if $img }}
  {{ $webp := $img.Resize "900x webp q85" }}
  {{ $avif := $img.Resize "900x avif q82" }}
  <figure>
    <picture>
      <source srcset="{{ $avif.RelPermalink }}" type="image/avif">
      <source srcset="{{ $webp.RelPermalink }}" type="image/webp">
      <img src="{{ $img.RelPermalink }}" alt="{{ .Get "alt" }}" loading="lazy" style="max-width:100%; height:auto;">
    </picture>
    {{ if .Get "caption" }}<figcaption>{{ .Get "caption" }}</figcaption>{{ end }}
  </figure>
{{ else }}
  <img src="{{ .Get "src" | relURL }}" alt="{{ .Get "alt" }}" loading="lazy">
{{ end }}

Code Blocks / Text Diagram Interception Engine (layouts/_default/_markup/render-codeblock.html)

Gracefully catches text diagram parameters to render vector schemas natively via standard markdown backtick calls:

{{ if eq .Type "mermaid" }}
  {{ .Page.Store.Set "hasMermaid" true }}
  <div class="mermaid">{{ .Inner | safeHTML }}</div>
{{ else }}
  {{ transform.Highlight .Inner .Type .Options }}
{{ end }}

Loads layout rendering components smoothly on demand:

{{ if .Store.Get "hasMermaid" }}
  <script type="module">
    import mermaid from '[https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs](https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs)';
    mermaid.initialize({ startOnLoad: true, theme: 'dark' });
  </script>
{{ end }}

The GitOps Pipeline (.forgejo/workflows/build.yaml)

This complete, production-hardened YAML definition coordinates the execution sequence. It targets standard runner spaces, locks module boundaries via structural GOTOOLCHAIN variables, strips sub-client nesting depths, and performs final production sync loops securely.

name: Build and Deploy Hugo Site

on:
  push:
    branches:
      - main
  schedule:
    # Commences build at 00:00 UTC every Saturday to check for upstream updates
    - cron: '0 0 * * 6'

jobs:
  hugo-build:
    runs-on: docker
    container:
      image: node:18-alpine
    
    steps:
      - name: Install System Dependencies and Latest Hugo
        run: |
          apk add --no-cache git wget jq libc6-compat libstdc++ openssh-client rsync ca-certificates go
          HUGO_VERSION=$(wget -qO- [https://api.github.com/repos/gohugoio/hugo/releases/latest](https://api.github.com/repos/gohugoio/hugo/releases/latest) | jq -r '.tag_name' | sed 's/v//')
          wget "[https://github.com/gohugoio/hugo/releases/download/v$](https://github.com/gohugoio/hugo/releases/download/v$){HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz"
          tar -xzf "hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz" hugo
          mv hugo /usr/local/bin/
          rm "hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz"

      - name: Checkout Source Code
        uses: actions/checkout@v4
        with:
          submodules: 'false'

      - name: Scan Repository for Secrets
        run: |
          echo "Initializing TruffleHog secret scanning engine..."
          # Fetch and install the official TruffleHog binary
          curl -sSfL https://github.com/trufflesecurity/trufflehog/releases/latest/download/trufflehog_linux_amd64.tar.gz | tar -xz -C /usr/local/bin
          
          # Scan the checked-out filesystem directories. 
          # The pipeline will fail immediately if a leaked key or secret is detected.
          trufflehog filesystem . --fail --only-verified

      - name: Scan Project for Vulnerabilities
        run: |
          echo "Commencing vulnerability scan..."
          # Freeze Go runtime mechanics to matching native local engine targets
          GOTOOLCHAIN=local go install golang.org/x/vuln/cmd/govulncheck@v1.0.4
          /root/go/bin/govulncheck ./... || echo "Security baseline scan completed with warnings."

      - name: Manually Clone PaperMod Theme
        run: |
          rm -rf themes/PaperMod
          git clone --depth=1 [https://github.com/adityatelange/hugo-PaperMod.git](https://github.com/adityatelange/hugo-PaperMod.git) themes/PaperMod

      - name: Fetch Latest FluffyChat Build (/wetter/)
        run: |
          echo "Fetching latest FluffyChat build for /wetter/..."
          rm -rf static/wetter
          mkdir -p static/wetter
          
          URL=$(wget -qO- [https://api.github.com/repos/krille-chan/fluffychat/releases/latest](https://api.github.com/repos/krille-chan/fluffychat/releases/latest) | jq -r '.assets[] | select(.name | contains("web")) | .browser_download_url' | head -n 1)
          
          if [ "$URL" != "null" ] && [ -n "$URL" ]; then
            echo "Downloading FluffyChat from: $URL"
            wget -q -O /tmp/fluffychat.tar.gz "$URL"
            tar -xzf /tmp/fluffychat.tar.gz -C static/wetter --strip-components=2 build/web || tar -xzf /tmp/fluffychat.tar.gz -C static/wetter --strip-components=1
            sed -i 's|<base href="/web/">|<base href="/wetter/">|g' static/wetter/index.html || true
            rm -f /tmp/fluffychat.tar.gz
            echo "FluffyChat staged successfully."
          else
            echo "CRITICAL: Could not resolve FluffyChat download asset target URL."
            exit 1
          fi

      - name: Fetch Latest Phanpy Build (/bbs/)
        run: |
          echo "Fetching latest Phanpy build for /bbs/..."
          rm -rf static/bbs
          mkdir -p static/bbs
          
          URL=$(wget -qO- [https://api.github.com/repos/cheeaun/phanpy/releases/latest](https://api.github.com/repos/cheeaun/phanpy/releases/latest) | jq -r '.assets[] | select(.name | contains("dist")) | .browser_download_url' | head -n 1)
          
          if [ "$URL" != "null" ] && [ -n "$URL" ]; then
            echo "Downloading Phanpy from: $URL"
            wget -q -O /tmp/phanpy.tar.gz "$URL"
            tar -xzf /tmp/phanpy.tar.gz -C static/bbs --strip-components=1
            rm -f /tmp/phanpy.tar.gz
            if [ -f static/bbs/index.html ]; then
              sed -i 's|<base href="/">|<base href="/bbs/">|g' static/bbs/index.html
            fi
            echo "Phanpy staged successfully."
          else
            echo "CRITICAL: Could not resolve Phanpy download asset target URL."
            exit 1
          fi

      - name: Compile Hugo Site Natively
        run: |
          rm -rf public/
          hugo --gc --minify --destination ./public
          echo "Deploy Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" > ./public/deploy.txt

      - name: Compile Pagefind Search Index Track
        run: |
          echo "Executing Pagefind post-processing compilation..."
          npx pagefind --site "public"

      - name: Apply Gzip Static Compression
        run: |
          echo "Applying gzip-static pre-compression layer..."
          find public/ -type f ! -name '*.png' ! -name '*.gz' | xargs -P $(nproc 2>/dev/null || echo 2) gzip -9kf

      - name: Deploy Securely over Local Network loop
        env:
          SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_KEY }}
        run: |
          mkdir -p ~/.ssh
          echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          
          TARGET_HOST=$(ip route | grep default | awk '{print $3}')
          echo "Deploying to host machine gateway: $TARGET_HOST on port 2222"
          
          ssh-keyscan -p 2222 -H "$TARGET_HOST" >> ~/.ssh/known_hosts
          rsync -avz --delete --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rw,Fg=r,Fo=r -e "ssh -p 2222" ./public/ nickers@"$TARGET_HOST":/home/nickers/docker/www/site-content/

Local Setup & Deployment

  1. Clone the Repository:
git clone [https://your-git-instance.domain.com/your-user/your-site.git](https://your-git-instance.domain.com/your-user/your-site.git)
cd your-site
  1. Test the Compilation Stack Natively: Verify template layout handling variables resolve cleanly prior to execution pushes:
hugo server --gc --minify
  1. Inject Deployment Secrets: Ensure your server control gate registers the authorized asymmetric private deployment key inside the repository workspace configuration keys under the variable identifier: DEPLOY_KEY.
  2. Push Code to Trigger the GitOps Cycle:
git add .
git commit -m "feat: infrastructure deployment lock"
git push origin main

License

Distributed under the MIT License. See LICENSE for more information.