My Blog Automation
Here is my cool way to post a blog right from my Obsidian, with less friction as possible. Check out!!
What I mean by Automation?
It is just a shell script which i run just after I finished typing my blog and my coffee. I just makes thing way more easier and also serves me the ego of having my own blog site. It is just cool. Here you go –>
Where do I type my Blog?
In Obsidian. If you are not using it then you have to give it a try it awesome and you get way more control of your data.
Here is my Pipeline
Just make a folder like this. Here you add all the you posts in the future.
I am using Ubuntu so all my commands are made to work on Ubuntu… If you are using something different then make necessary changes.
Set up Prerequisites
- Install Go
- Install Hugo
- Install Git
That’s it. Google them and follow the instructions.
Set up Hugo Site
Go to your terminal ctrl+alt+t
in Linux.
There go to the directory of your choice and
hugo new site mysite
cd mysite
git init
here mysite
can be anything you wish.
Add theme
For theme I chose hugo-brewm
. It just looks cool. Here are the instruction and this applies to most of the Hugo theme.
Add git-sub-module
git submodule add https://github.com/foxihd/hugo-brewm themes/hugo-brewm
Inside hugo.toml
## Base URL for the site
baseURL = "https://prathamngundikere.github.io/"
## Site title
title = 'Pratham'
## Use hugo-brewm theme
theme = 'hugo-brewm'
## Enable Git information for pages, (e.g. lastMod date information)
enableGitInfo = true
## Convert all URLs to absolute URLs
canonifyURLs = true
## Default language for content
defaultContentLanguage = 'en'
## Put default language in subdirectory
defaultContentLanguageInSubdir = false
## Use sections for main menu
sectionPagesMenu = 'main'
## Files to ignore when building site
ignoreFiles = [ '\.redacted', '\.old','\.bak', '\.tmp', '\.swp', '\.DS_Store']
## Enable code fence highlighting
[markup]
[markup.highlight]
codeFences = true
## Sitemap configuration
[sitemap]
## Change frequency setting (will affect posts listings layout): 'always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'
changeFreq = 'monthly'
## Site parameters
[params]
## Site title
title = "Pratham"
## Site description
description = "An ExampleSite built with Hugo and Hugo-Brewm theme"
## Copyright notice
copyright = "Copyright 2025 (c) Pratham"
## Enable extended metadata (social cards)
extMeta = true
## Enable coffee metric
coffeeStat = true
## Default social card image, recommended resolution: 1200 x 630px
# images = "example.com/img/social-share.jpg"
## Author information
[params.author]
## site author's name
name = 'Pratham N Gundikere'
## Author's email
email = 'prathamngundikere@yahoo.com'
## Other method to customize author and co-authors information
#coauthor = [
# {name = "A.N. Other", bio = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."}
#]
## Comments configuration
[params.comments]
## Disable comments (disable fediverse comments)
disabled = true
## Comment platform selection, currently only 'fediverse' is supported, further options to be determined
# platform = 'fediverse'
## Fediverse integration settings
[params.fediverse]
## Fediverse instance URL
#instance = 'example.com'
## Fediverse username
#username = 'username'
## Logo configuration
[params.logo]
## Light mode logo mark
#logoMark = 'https://example.com/logoMark.svg'
## Dark mode logo mark
#logoMarkDark = 'https://example.com/logoMarkDark.svg'
## Enable logo type
logoType = true
## Feed display settings
[params.feed]
## Enable flowlines
flowlines = true
## Limit number of flowlines with maximum 42
flowlinesLimit = 21
## Home page display settings
[params.home]
## Disable slide carousel
disableSlide = false
## Disable taxonomy listing carousel
disableListing = false
## Post display settings
[params.posts]
## Enable text justification
justifying = false
## Disable paragraph indentation
noIndent = false
## Show colophon section (including QR code)
colophon = true
## disable redaction history
disableHistory = false
## SHow related content
related = true
## Show share buttons
share = true
## Search configuration
[params.search]
## Enable search functionality, use duckduckgo (no javascript)
enable = true
## Use pagefind search when javascript enabled, currently only 'pagefind' is supported, further options to be determined
pagefind = true
## fallback searchbox when javascript disabled, currently only 'duckduckgo' is supported, further options to be determined
fallback = 'duckduckgo'
## Typography settings
[params.typeface]
## Use web safe fonts (will overide font selection below)
webSafe = false
## Serif font selection
roman = 'crimson'
## Sans-serif font selection
sans = 'inter'
## Extended Metadata and Social card configuration
[params.socialCard]
## Enable twitter and opengraph social cards (same as .params.extMeta)
enable = true
## Default social card image, same as .Params.images
# images = "img/social-share.jpg"
## Enable Twitter cards
twitter = true
## Twitter creator handle
twitterCreator = "@prathamng"
## Twitter site handle
twitterSite = "@prathamng"
## Enable OpenGraph
# opengraph = true
## Facebook App ID
# facebookAppID = "123456789"
## Facebook Admin ID
# facebookAdmin = "USER_ID"
## Schema.org (EXPERIMENTAL, not fully supported body tags)
# schema = true
## JsonLD (EXPERIMENTAL, cannot validate permalink)
# jsonLD = true
[[menu.footer]]
identifier = "github"
name = "GitHub"
url = "https://github.com/prathamngundikere"
pre = "github"
[[menu.footer]]
identifier = "linkedin"
name = "LinkedIn"
url = "https://in.linkedin.com/in/prathamngundikere"
pre = "linkedin"
[[menu.footer]]
identifier = "instagram"
name = "Instagram"
url = "https://www.instagram.com/prathamngundikere"
pre = "instagram"
[[menu.footer]]
identifier = "x"
name = "X"
url = "https://x.com/prathamng"
pre = "x"
And changes theses as you wish.
Then In terminal under the site directory
hugo server
If it is all good then it runs.
In the static directory create a folder called images
Now create a file structure in the site directory –> .github/workflows/hugo.yml
# Workflow for building and deploying a Hugo site to GitHub Pages
name: Deploy Hugo Site to Pages
on:
# Runs on pushes targeting the default branch
push:
branches:
- master
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
# Default to bash
defaults:
run:
shell: bash
jobs:
# Build job
build:
runs-on: ubuntu-24.04-arm
env:
HUGO_VERSION: 0.143.1
steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-arm64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Checkout
uses: actions/checkout@v4.2.2
with:
submodules: recursive
fetch-depth: 0
- name: Setup Pages
id: pages
uses: actions/configure-pages@v5
- name: Install Node.js dependencies
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
- name: Build with Hugo
env:
# For maximum backward compatibility with Hugo modules
HUGO_ENVIRONMENT: production
HUGO_ENV: production
run: |
hugo \
--gc \
--minify \
--baseURL "${{ steps.pages.outputs.base_url }}/"
- name: Index pagefind
run: npx pagefind --source "./public"
- name: Upload artifact
uses: actions/upload-pages-artifact@v3.0.1
with:
path: ./public
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-24.04-arm
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4.0.5
Now all is set. It is time for Python Script to copy images from Obsidian attachment folder to your site. I got this from NetworkChuck. Make a image,py
in you home directory (recommended)
import os
import re
import shutil
# Paths
posts_dir = "/home/prathamngundikere/PrathamBlog/content/post"
attachments_dir = "/home/prathamngundikere/Obsidian/4 - Attachments"
static_images_dir = "/home/prathamngundikere/PrathamBlog/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 format 
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.")
Make changes with your own file locations.
Note : Add you git and push it GitHub atleast once before you run mega script. I cannot spoon feed you everything
Now time for MEGA SCRIPT. In your home directory create a file sync.sh
#!/bin/bash
set -euo pipefail
# Change to the script's directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Set paths
sourcePath="/home/prathamngundikere/Obsidian/post"
destinationPath="/home/prathamngundikere/PrathamBlog/content"
blogPath="/home/prathamngundikere/PrathamBlog"
# Check for required commands
for cmd in git rsync python3; do
if ! command -v $cmd &> /dev/null; then
echo "$cmd is not installed or not in PATH."
exit 1
fi
done
# Step 1: 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 2: Process Markdown files with Python script
echo "Processing image links in Markdown files..."
if ! python3 images.py; then
echo "Failed to process image links."
exit 1
fi
# Step 3: Change to blog directory and handle Git operations
cd "$blogPath"
# Step 4: Add changes to Git
echo "Staging changes for Git..."
git add .
# Step 5: Extract blog title from git status and commit
# Get the name of the modified/new .md file
modified_file=$(git status --porcelain | grep "content/post.*\.md" | head -n 1 | sed 's/^...//g')
if [ -n "$modified_file" ]; then
# Extract just the filename without path and extension
blog_title=$(basename "$modified_file" .md)
commit_message="$blog_title $(date +'%d-%m-%Y')"
else
# Fallback if no .md file is found
commit_message="Blog update $(date +'%d-%m-%Y')"
fi
echo "Committing with message: $commit_message"
git commit -m "$commit_message"
# Step 6: Push changes to master branch
echo "Pushing to GitHub..."
if ! git push origin master; then
echo "Failed to push to master branch."
exit 1
fi
echo "All done! Site synced, processed, and deployed."
If everything is good, It should work. If not, don’t worry you will figure it out. You are smart.
Alright thank you for coming down this far. See you in next one
BYE!!!