Complete Guide to Building a Hugo Blog Pipeline

Brad | Jan 13, 2025 min read

Image

This is a notes page for how this blog was created. It is in reference to the NetworkChuck Blog Video

I have added some additional notes to clarify some areas that I originally stumbled on to get everything working. I also removed some unnecessary steps.

Prepare Obsidian

  1. Download and Install Obsidian
  2. Create a ‘posts’ folder in Obsidian root
  3. Create an ‘images’ directory in Obsidian root
  4. Open Obsidian Settings to Tell Obsidian to use the new ‘images’ directory to store and reference images.

Add Some Frontmatter to a Page

This allows the template to display meta data about your blog post. It should be placed at the top of your Obsidian note. To add this ‘Frontmatter’ meta data, put the Obsidian note page into ‘Source mode’ by using the 3 dots menu at the top right of the page.

---
title: blogtitle
date: 2024-11-06
draft: false
tags:
  - tag1
  - tag2
---

Install and Configure Hugo and GitHub Repo

Install Hugo

sudo apt install hugo

Verify Hugo works

hugo version

Create a new Hugo site

hugo new site websitename
cd websitename

Install and Configure a GitHub Repo

Create a new repository for your project

git init

Add all files in website directory to git

git add .

Commit the files

git commit -m "Initial commit"

Download the Terminal Theme as a Git Submodule

git submodule add -f https://github.com/panr/hugo-theme-terminal.git themes/terminal

Update the ‘hugo.toml’ file at the root of the hugo site Note: The config on the github fails since it is looking for a module. The NetworkChuck code works, but the 6th line needs to be replaced with ‘pagerSize = 5’

baseurl = "/"
languageCode = "en-us"
# Add it only if you keep the theme in the `themes` directory.
# Remove it if you use the theme as a remote Hugo Module.
theme = "terminal"
pagerSize = 5

[params]
  # dir name of your main content (default is `content/posts`).
  # the list of set content will show up on your index page (baseurl).
  contentTypeName = "posts"

  # if you set this to 0, only submenu trigger will be visible
  showMenuItems = 2

  # show selector to switch language
  showLanguageSelector = false

  # set theme to full screen width
  fullWidthTheme = false

  # center theme with default width
  centerTheme = false

  # if your resource directory contains an image called `cover.(jpg|png|webp)`,
  # then the file will be used as a cover automatically.
  # With this option you don't have to put the `cover` param in a front-matter.
  autoCover = true

  # set post to show the last updated
  # If you use git, you can set `enableGitInfo` to `true` and then post will automatically get the last updated
  showLastUpdated = false

  # Provide a string as a prefix for the last update date. By default, it looks like this: 2020-xx-xx [Updated: 2020-xx-xx] :: Author
  # updatedDatePrefix = "Updated"

  # whether to show a page's estimated reading time
  # readingTime = false # default

  # whether to show a table of contents
  # can be overridden in a page's front-matter
  # Toc = false # default

  # set title for the table of contents
  # can be overridden in a page's front-matter
  # TocTitle = "Table of Contents" # default


[params.twitter]
  # set Twitter handles for Twitter cards
  # see https://developer.twitter.com/en/docs/tweets/optimize-with-cards/guides/getting-started#card-and-content-attribution
  # do not include @
  creator = ""
  site = ""

[languages]
  [languages.en]
    languageName = "English"
    title = "Terminal"

    [languages.en.params]
      subtitle = "A Hobo Theme"
      owner = ""
      keywords = ""
      copyright = ""
      menuMore = "Show more"
      readMore = "Read more"
      readOtherPosts = "Read other posts"
      newerPosts = "Newer posts"
      olderPosts = "Older posts"
      missingContentMessage = "Page not found..."
      missingBackButtonLabel = "Back to home page"
      minuteReadingTime = "min read"
      words = "words"

      [languages.en.params.logo]
        logoText = "Terminal"
        logoHomeLink = "/"

      [languages.en.menu]
        [[languages.en.menu.main]]
          identifier = "services"
          name = "Professional Services"
          url = "/services"
        [[languages.en.menu.main]]
          identifier = "photography"
          name = "Photography"
          url = "/photography"

Images.py Script

Create a file in the project root called ‘images.py’ and place this code:

import os
import re
import shutil

# Paths
# posts_dir is the site's post directory, not Obsidians
posts_dir = "/path/to/blog/posts"
attachments_dir = "/path/to/obsidian/images"
static_images_dir = "/path/to/blog/static/images"

# Step 1: Process each markdown file in the posts directory
for filename in os.listdir(posts_dir):
    if filename.endswith(".md"):
        filepath = os.path.join(posts_dir, filename)
        
        with open(filepath, "r") as file:
            content = file.read()
        
        # Step 2: Find all image links in the markdown
        images = re.findall(r'\[\[([^]]*\.png)\]\]', content)
        
        # Step 3: Replace image links and ensure URLs are correctly formatted
        for image in images:
            # Prepare the Markdown-compatible link with %20 replacing spaces
            markdown_image = f"[Image Description](/images/{image.replace(' ', '%20')})"
            content = content.replace(f"[[{image}]]", markdown_image)
            
            # Step 4: Copy the image to the Hugo static/images directory if it exists
            image_source = os.path.join(attachments_dir, image)
            if os.path.exists(image_source):
                shutil.copy(image_source, static_images_dir)

        # Step 5: Write the updated content back to the markdown file
        with open(filepath, "w") as file:
            file.write(content)

print("Markdown files processed and images copied successfully.")

MEGA SCRIPT

Create a file in the project root called ‘megascript.sh’ and place this code:

#!/bin/zsh
set -euo pipefail

# Change to the script's directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"

# Set variables for Obsidian to Hugo copy
sourcePath="/path/to/obsidian/posts"
destinationPath="/path/to/blog/content/directory"

# Set GitHub Repo
myrepo="reponame"

# Check for required commands
for cmd in git rsync python3 hugo; do
    if ! command -v $cmd &> /dev/null; then
        echo "$cmd is not installed or not in PATH."
        exit 1
    fi
done

# Step 1: Check if Git is initialized, and initialize if necessary
if [ ! -d ".git" ]; then
    echo "Initializing Git repository..."
    git init
    git remote add origin $myrepo
else
    echo "Git repository already initialized."
    if ! git remote | grep -q 'origin'; then
        echo "Adding remote origin..."
        git remote add origin $myrepo
    fi
fi

# Step 2: Sync posts from Obsidian to Hugo content folder using rsync
echo "Syncing posts from Obsidian..."

if [ ! -d "$sourcePath" ]; then
    echo "Source path does not exist: $sourcePath"
    exit 1
fi

if [ ! -d "$destinationPath" ]; then
    echo "Destination path does not exist: $destinationPath"
    exit 1
fi

rsync -av --delete "$sourcePath" "$destinationPath"

# Step 3: Process Markdown files with Python script to handle image links
echo "Processing image links in Markdown files..."
if [ ! -f "images.py" ]; then
    echo "Python script images.py not found."
    exit 1
fi

if ! python3 images.py; then
    echo "Failed to process image links."
    exit 1
fi

# Step 4: Build the Hugo site
echo "Building the Hugo site..."
if ! hugo; then
    echo "Hugo build failed."
    exit 1
fi

# Step 5: Add changes to Git
echo "Staging changes for Git..."
if git diff --quiet && git diff --cached --quiet; then
    echo "No changes to stage."
else
    git add .
fi

# Step 6: Commit changes with a dynamic message
commit_message="New Blog Post on $(date +'%Y-%m-%d %H:%M:%S')"
if git diff --cached --quiet; then
    echo "No changes to commit."
else
    echo "Committing changes..."
    git commit -m "$commit_message"
fi

# Step 7: Push all changes to the main branch
echo "Deploying to GitHub Main..."
if ! git push origin main; then
    echo "Failed to push to main branch."
    exit 1
fi

# Step 8: Push the public folder to the hostinger branch using subtree split and force push
echo "Deploying to GitHub Hostinger..."
if git branch --list | grep -q 'hostinger-deploy'; then
    git branch -D hostinger-deploy
fi

if ! git subtree split --prefix public -b hostinger-deploy; then
    echo "Subtree split failed."
    exit 1
fi

if ! git push origin hostinger-deploy:hostinger --force; then
    echo "Failed to push to hostinger branch."
    git branch -D hostinger-deploy
    exit 1
fi

git branch -D hostinger-deploy

echo "All done! Site synced, processed, committed, built, and deployed."

# Display a notification on successful completion MAC OS ONLY

if [[ $? -eq 0 ]]; then
	osascript -e 'display notification "MEGAScript completed successfully!" with title "Success" sound name "Funk"'
fi

Make the Script executable

chmod +x megascript.sh

Execute Script

./megascript.sh

Start Hugo Local Server

hugo server

Navigate to test locally or go to your site to see the changes live in production: http://localhost:1313/

Cheers! You’re done and your new blog should be deployed! 🥂

Image

Generate your own Theme

  1. Go to terminal-css
  2. Download the generate CSS file.
  3. Replace file /themes/terminal/assets/css/terminal.css with the CSS file you downloaded.

Fixes if you get GIT merge errors or something weird happens with the data pipeline.

Delete Git Branch

git push -d <remote_name> <branchname>   # Delete remote
git branch -d <branchname>               # Delete local

Navigate to:Hostinger

Go to: Advanced > GIT > Under Manage Repositories > Delete the repository reference

Navigate to the public_html of your site and delete all the files that get generated. Be sure to delete hidden .git folders.

Run ./megascript.sh to save and sync everything and setup a new clean ‘hostinger’ branch.

Go back to the Hostinger GIT settings and add the reference to the new ‘hostinger’ branch.

Run ./megascript.sh again and it should sync all of your changes to your new blog.

Switching to TeXify theme

Create Hugo Site and GIT Init

hugo new site SITE_NAME && cd SITE_NAME && git init

Install theme GIT module

git submodule add https://github.com/michaelneuper/hugo-texify3.git themes/hugo-texify3

Install required Node.js packages

npm i -D postcss postcss-cli autoprefixer

Install postcss-import module

npm install --save-dev postcss-import

Install PostCSS

npm i -D postcss postcss-cli autoprefixer