Skip to main content

Hugo

··474 words·3 mins·
Michael
Author
Michael
some dude that works on datacenters, plays guitar, streams, has a lot of side projects and unhealthy addiction to ow

Project: Hugo Documentation Framework (Blowfish)
#

This repository (hugo-framework) manages the core Hugo build environment (engine, Blowfish theme, layouts, partials, and base configuration) for both the Public and Private Wikis.

🏗️ Architecture
#

To allow for seamless Dev vs. Prod workflows, the architecture is split into three repositories:

  1. Framework (/srv/dev/hugo/wiki): Contains the Hugo engine, themes, custom shortcodes (like include), and the default configuration.
  2. Public Content (/srv/docs/public): Contains the Markdown files and assets for the public-facing wiki.
  3. Private Content (/srv/docs/private): Contains the Markdown files and assets for the internal/private wiki.

Dynamic Configuration Merging: The Framework defines the base styling in config/_default/params.toml. However, the Content repositories can dynamically override settings during the build:

  • Menus: By providing a config/_default/menus.en.toml file.
  • Theme Settings (Edit Links): Injected via Environment Variables (HUGO_PARAMS_ARTICLE_EDITURL) during the CI/CD pipeline to avoid hardcoding repository URLs in the base framework.

💻 Local Development (“Live Link”)#

To write documentation with real-time previews, use symbolic links on your host machine to connect a content repository to the framework:

  1. Establish Symlinks (Example for Public Docs):
    cd /srv/dev/hugo/wiki
    rm -rf content static
    ln -s /srv/docs/public content
    ln -s /srv/docs/public/static static
  2. Preview Changes:
    hugo server --bind 0.0.0.0 --appendPort=false --baseURL="/"

🚀 CI/CD Pipeline & Deployment
#

Deployment is fully automated through Gitea Actions stored in the Content repositories (e.g., .gitea/workflows/deploy.yaml). Manual builds to production should be avoided.

The Build Process:

  1. The Gitea Runner checks out the triggering content repository.
  2. It clones this Framework repository dynamically.
  3. It injects the Markdown files and static assets into the framework.
  4. It builds the static HTML site using Hugo.
  5. The generated files are copied directly into the persistent web server volumes:
    • Public Wiki: /srv/www/docs-public
    • Private Wiki: /srv/www/docs-private

These directories are then served by their respective Dockerized Nginx containers.

📂 File Inclusions (Absolute Paths)
#

To allow for the inclusion of raw files outside of the standard Hugo content/ directory (such as system configurations located in /srv/configs), this framework utilizes Hugo Module Mounts mapping into the virtual assets/ directory.

How it Works
#

Hugo’s readFile and fileExists functions are blocked by strict OS security policies and cannot read paths outside the project root. To bypass this safely:

  1. The external directory is mounted in config/_default/hugo.toml:
    [[module.mounts]]
      source = "/srv/configs"
      target = "assets/srv/configs"
  2. The custom include.html shortcode ({{< include "/srv/configs/..." >}}) detects paths starting with /srv/, trims the leading slash, and uses resources.Get to fetch the raw file from the virtual assets/ namespace, completely bypassing the Markdown parser and security blocks.

⚠️ Critical Note on CI/CD for External Files: If you map new external directories in hugo.toml (e.g., /srv/newfolder), the Docker container running the Gitea Action must also have OS-level access to those files. You must:

  1. Allow the volume in the host’s Gitea runner config (/srv/gitea/runner/config.yaml -> valid_volumes).
  2. Mount the volume in the job options of the content repository’s .gitea/workflows/deploy.yaml (e.g., options: --user root -v /srv/www:/srv/www -v /srv:/srv:ro).